면접을 보고 오느라 개발을 하지 못했습니다.

'개발일지' 카테고리의 다른 글

20.09.12 비개발일지  (0) 2020.09.12
20.09.11 비개발일지  (0) 2020.09.11
20.09.03 비개발일지  (0) 2020.09.03
20.09.01 비개발일지  (0) 2020.09.01
20.08.29 비개발일지  (0) 2020.08.29

조급했던 개발을 기한 없이 개발 하자는 생각을 하니까 모든 것이 편해졌습니다.

일단은 SubObject의 Replicate를 보면서 조금씩 문서를 보고 Replicate를 다듬고 있습니다.

하지만 Component Replicate로 해결이 되지 않을까 하는 생각이 계속 멤돕니다.

Block Trap은 SubObject Replicate 없이 잘 동작 중입니다.

또한 새로운 방식으로는 Visibility만 잘 동작 안하고, Collision은 Character가 Host에서 동작하지 않습니다.

살펴보고 고려해야 할 부분은 많지만 어느정도 마음을 놓아놓고 면접 결과가 다 나올 때까지 편하게 개발하겠습니다.

'개발일지 > Treasure Hunter' 카테고리의 다른 글

20.09.14 개발일지  (0) 2020.09.14
20.09.07 개발일지  (0) 2020.09.07
20.09.05 개발일지  (0) 2020.09.05
20.09.02 개발일지  (0) 2020.09.02
20.08.21 개발일지  (0) 2020.08.31

이제 우리가 봐야 할 남은 코드는 두 가지입니다.

하나는 DirectX12 예시 프로젝트의 기반이 되는 Base Code인 D3DApp.

그리고 다른 하나는 이번 Box Example의 코드인 BoxApp.

 

이 글에서는 D3DApp.h를 보겠습니다. 전체 코드는 꽤 기니까 접은글로 올리겠습니다.

더보기
#pragma once
#include "Common.h"

class D3DApp
{
protected:
	D3DApp(HINSTANCE hInstance);
	D3DApp(const D3DApp& rhs) = delete;
	D3DApp& operator=(const D3DApp& rhs) = delete;
	virtual ~D3DApp();

public:
	static D3DApp* GetApp();
	HINSTANCE	AppInst() const;
	HWND		MainWnd() const;
	float		AspectRatio() const;

	bool Get4xMsaaState() const;
	void Set4xMsaaState(bool value);

	int run();

	virtual bool Initialize();
	virtual LRESULT MsgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

protected:
	virtual void CreateRtvAndDsvDescriptorHeaps();
	virtual void OnResize();
	virtual void Update(const GameTimer& gt) = 0;
	virtual void Draw(const GameTimer& gt) = 0;

	virtual void OnMouseDown(WPARAM btnState, int x, int y) = 0;
	virtual void OnMouseUp(WPARAM btnState, int x, int y) = 0;
	virtual void OnMouseMove(WPARAM btnState, int x, int y) = 0;

protected:
	bool InitMainWindow();
	bool InitDirect3D();
	void CreateCommandObjects();
	void CreateSwapChain();

	void FlushCommandQueue();

	ID3D12Resource* CurrentBackBuffer() const;
	D3D12_CPU_DESCRIPTOR_HANDLE CurrentBackBufferView() const;
	D3D12_CPU_DESCRIPTOR_HANDLE DepthStencilView() const;

	void CalculateFrameStats();

	void LogAdpaters();
	void LogAdpaterOutputs(IDXGIAdapter* adapter);
	void LogOutputDisplayModes(IDXGIOutput* output, DXGI_FORMAT format);

private:
	ATOM MyRegisterClass(WNDCLASSEXW& wc);
	BOOL InitInstance(int nCmdShow);
	INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
protected:
	static D3DApp* mApp;

	HINSTANCE	mhAppInst = nullptr;
	HWND		mhMainWnd = nullptr;
	bool		mAppPaused = false;
	bool		mMinimized = false;
	bool		mMaximized = false;
	bool		mResizing = false;
	bool		mFullscreenState = false;

	bool		m4xMsaaState = false;
	UINT		m4xMsaaQuality = 0;

	GameTimer mTimer;

	Microsoft::WRL::ComPtr<IDXGIFactory4> mdxgiFactory;
	Microsoft::WRL::ComPtr<IDXGISwapChain> mSwapChain;
	Microsoft::WRL::ComPtr<ID3D12Device> md3dDevice;

	Microsoft::WRL::ComPtr<ID3D12Fence> mFence;
	UINT64 mCurrentFence = 0;

	Microsoft::WRL::ComPtr<ID3D12CommandQueue> mCommandQueue;
	Microsoft::WRL::ComPtr<ID3D12CommandAllocator> mDirectCmdListAlloc;
	Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList> mCommandList;

	static const int SwapChainBufferCount = 2;
	int mCurrentBackBuffer = 0;
	Microsoft::WRL::ComPtr<ID3D12Resource> mSwapChainbuffer[SwapChainBufferCount];
	Microsoft::WRL::ComPtr<ID3D12Resource> mDepthStencilBuffer;

	Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> mRtvHeap;
	Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> mDsvHeap;

	D3D12_VIEWPORT mScreenViewport;
	D3D12_RECT mScissorRect;

	UINT mRtvDescriptorSize = 0;
	UINT mDsvDescriptorSize = 0;
	UINT mCbvSrvUavDescriptorSize = 0;

	std::wstring mMainWndCaption = L"d3d App";
	D3D_DRIVER_TYPE md3dDriverType = D3D_DRIVER_TYPE_HARDWARE;
	DXGI_FORMAT mBackBufferFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
	DXGI_FORMAT mDepthStencilFormat = DXGI_FORMAT_D24_UNORM_S8_UINT;
	int mClientWidth = 960;
	int mClientHeight = 540;
};

우선 생성자와 소멸자 부분을 보면 복사생성자, 대입연산자가 막혀있고 Protected로 선언되어 있습니다.

protected:
	D3DApp(HINSTANCE hInstance);
	D3DApp(const D3DApp& rhs) = delete;
	D3DApp& operator=(const D3DApp& rhs) = delete;
	virtual ~D3DApp();

이는 원본 코드에서 Main 함수를 포함하고 있었기에 Singleton으로 구현이 되어있기 때문입니다.

이에 대한 흔적은 static으로 함수와 변수들로부터 그 흔적을 발견할수도 있습니다.

물론 원래는 모두 제거해야 할 흔적들이나, 미처 하지 못했고, 나중에 시간이 남으면 한번 해보겠습니다.

 

그 다음 Public 부분.

public:
	static D3DApp* GetApp();
	HINSTANCE	AppInst() const;
	HWND		MainWnd() const;
	float		AspectRatio() const;

	bool Get4xMsaaState() const;
	void Set4xMsaaState(bool value);

	int run();

	virtual bool Initialize();
	virtual LRESULT MsgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

DirectX에서의 초기화 부분과 Window 생성 부분의 함수들이 혼용되고 있습니다.

역시 나중에 절반이 사라질 예정입니다.

 

이 중 run 함수는 실질적인 Rendering을 시작하는 함수이고, 

Initialize는 초기화 함수입니다.

보통은 생성자에서는 변수만 초기화 하고, 초기화 함수를 두어 DirectX 설정을 따로 초기화 합니다.

예시에서도 그렇고, 상용 게임 엔진에서도 BeginPlay라는 함수가 따로 존재합니다.

보통 "Compile 상에서 초기화 하느냐"와 "Runtime 상에서 초기화 하느냐"로 나뉘어 집니다.

 

그 아래 함수들을 보겠습니다.

protected:
	virtual void CreateRtvAndDsvDescriptorHeaps();
	virtual void OnResize();
	virtual void Update(const GameTimer& gt) = 0;
	virtual void Draw(const GameTimer& gt) = 0;

	virtual void OnMouseDown(WPARAM btnState, int x, int y) = 0;
	virtual void OnMouseUp(WPARAM btnState, int x, int y) = 0;
	virtual void OnMouseMove(WPARAM btnState, int x, int y) = 0;

 

함수들이 혼용되어 있는데 On으로 시작하는 함수는 모두 Window Event 관련 함수라 보시면 됩니다.

아마 Mouse 입력 관련된 함수들은 그대로 사용되겠지만,

OnResize는 밖으로 떨어져 나가면 꽤 많은 입력값을 요구할 것입니다.

 

Update는 매 Tick마다 발생하는 변화를 적용하고, Draw는 그때마다 프레임들을 새로 그려줍니다.

CreateRtvAndDsvDescriptorHeaps는 Renter Target View와 Depth Stencil View의 Descriptor Heaps를 생성해준다.

Descriptor는 Resource를 GPU에 서술하는 자료구조이다.

Descriptor Heap은 이런 Descriptor의 배열로, CPU에 의해서만 즉시 편집될 수 있고 GPU에서는 편집을 할 수 없다.

 

이에 대해 아직 설명을 한 적이 없는데 중간에 책을 바꿔서 누락이 된건지, 코드 설명과 같이 나오는 이론인지 모르겠다.

그러니 우선 참고자료를 첨부하고 넘어가겠다.

docs.microsoft.com/ko-kr/windows/win32/direct3d12/resource-binding-flow-of-control

 

리소스 바인딩 개요 - Win32 apps

DirectX 12에서 리소스 바인딩의 핵심은 설명자, 설명자 테이블, 설명자 힙 및 루트 서명 개념입니다.

docs.microsoft.com

docs.microsoft.com/ko-kr/windows/win32/direct3d12/descriptor-heaps-overview?redirectedfrom=MSDN

 

설명자 힙 개요 - Win32 apps

설명자 힙은 PSO(파이프라인 상태 개체)에 속하지 않는 많은 개체 유형[예: SRV(셰이더 리소스 뷰), UAV(순서가 지정되지 않은 액세스 뷰), CBV(상수 버퍼 뷰) 및 샘플러]을 포함합니다.

docs.microsoft.com

hannom.tistory.com/158

 

[DirectX12]기본지식 - 자원과 서술자(resource와 descriptor)

렌더링 과정에서 GPU는 자원들에 자료를 기록하거나 자원(resource)들에서 자료를 읽어들이고 그리기 명령을 제출하기 전에 먼저 해당 그리기 호출이 참조한 자원들을 렌더링 파이프라인에 묶어야

hannom.tistory.com

 

다음 선언부를 살펴봅시다.

protected:
	bool InitMainWindow();
	bool InitDirect3D();
	void CreateCommandObjects();
	void CreateSwapChain();

	void FlushCommandQueue();

	ID3D12Resource* CurrentBackBuffer() const;
	D3D12_CPU_DESCRIPTOR_HANDLE CurrentBackBufferView() const;
	D3D12_CPU_DESCRIPTOR_HANDLE DepthStencilView() const;

	void CalculateFrameStats();

	void LogAdpaters();
	void LogAdpaterOutputs(IDXGIAdapter* adapter);
	void LogOutputDisplayModes(IDXGIOutput* output, DXGI_FORMAT format);

InitMainWindow는 당연히 Window 초기화 함수입니다.

코드는 작성할 것이지만 자세한 설명은 생략하겠습니다.

InitDirect3D는 DirectX와 관련된 초기화를 담당합니다.

그 밑에 함수들은 이름 그대로의 긴으을 가지고 있습니다.

 

CalculateFrameStats부터 아래의 함수들은 기능과는 상관 없는 함수들입니다.

예시 코드에서 기본 성능을 화면에 출력하는데 이와 관련된 함수들입니다.

별도로 수정 될 이유도 없고, 있는 그대로 적어 두시면 될 것입니다.

 

마지막 선언부입니다.

private:
	ATOM MyRegisterClass(WNDCLASSEXW& wc);
	BOOL InitInstance(int nCmdShow);
	INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);

Window 관련 함수들입니다. 가볍게 무시하고 넘어가겠습니다.

 

남은 것은 변수들입니다.

protected:
	static D3DApp* mApp;

	HINSTANCE	mhAppInst = nullptr;
	HWND		mhMainWnd = nullptr;
	bool		mAppPaused = false;
	bool		mMinimized = false;
	bool		mMaximized = false;
	bool		mResizing = false;
	bool		mFullscreenState = false;

	bool		m4xMsaaState = false;
	UINT		m4xMsaaQuality = 0;

	GameTimer mTimer;

	Microsoft::WRL::ComPtr<IDXGIFactory4> mdxgiFactory;
	Microsoft::WRL::ComPtr<IDXGISwapChain> mSwapChain;
	Microsoft::WRL::ComPtr<ID3D12Device> md3dDevice;

	Microsoft::WRL::ComPtr<ID3D12Fence> mFence;
	UINT64 mCurrentFence = 0;

	Microsoft::WRL::ComPtr<ID3D12CommandQueue> mCommandQueue;
	Microsoft::WRL::ComPtr<ID3D12CommandAllocator> mDirectCmdListAlloc;
	Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList> mCommandList;

	static const int SwapChainBufferCount = 2;
	int mCurrentBackBuffer = 0;
	Microsoft::WRL::ComPtr<ID3D12Resource> mSwapChainbuffer[SwapChainBufferCount];
	Microsoft::WRL::ComPtr<ID3D12Resource> mDepthStencilBuffer;

	Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> mRtvHeap;
	Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> mDsvHeap;

	D3D12_VIEWPORT mScreenViewport;
	D3D12_RECT mScissorRect;

	UINT mRtvDescriptorSize = 0;
	UINT mDsvDescriptorSize = 0;
	UINT mCbvSrvUavDescriptorSize = 0;

	std::wstring mMainWndCaption = L"d3d App";
	D3D_DRIVER_TYPE md3dDriverType = D3D_DRIVER_TYPE_HARDWARE;
	DXGI_FORMAT mBackBufferFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
	DXGI_FORMAT mDepthStencilFormat = DXGI_FORMAT_D24_UNORM_S8_UINT;
	int mClientWidth = 960;
	int mClientHeight = 540;

GameTimer 위의 선언부와 가장 아래 선언부 대부분은 Window 생성과 관련된 변수들입니다.

이들을 무시하고, 대부분은 이름을 보면 어떤 것인지 알 수 있을 것입니다.

 

하나하나 설명을 해도 좋겠지만 그러지 않는 이유는

1. 제가 아직 그 정도로 머리에 다 채워넣지 못했습니다.

아직은 보고 이해를 하는 수준이라서 글이 읽기 편하게 작성이 되지는 않습니다.

2. 보면 아는 내용을 굳이 적어서 시간을 낭비하고 글을 길게 늘리고 싶지 않습니다.

 

그러니 구현부로 넘어가보겠습니다.

더보기
#include "D3DApp.h"
#include <cassert>

using namespace std;
using namespace Microsoft::WRL;

LRESULT CALLBACK
MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	return D3DApp::GetApp()->MsgProc(hwnd, msg, wParam, lParam);
}

D3DApp* D3DApp::mApp = nullptr;
D3DApp* D3DApp::GetApp()
{
	return mApp;
}

D3DApp::D3DApp(HINSTANCE hInstance) : mhAppInst(hInstance)
{
	assert(mApp == nullptr);
	mApp = this;
}

D3DApp::~D3DApp()
{
	if (md3dDevice != nullptr)
	{
		FlushCommandQueue();
	}
}

HINSTANCE D3DApp::AppInst() const
{
	return mhAppInst;
}

HWND D3DApp::MainWnd() const
{
	return mhMainWnd;
}

float D3DApp::AspectRatio() const
{
	return static_cast<float>(mClientWidth) / mClientHeight;
}

bool D3DApp::Get4xMsaaState() const
{
	return m4xMsaaState;
}

void D3DApp::Set4xMsaaState(bool value)
{
	if (m4xMsaaState != value)
	{
		m4xMsaaState = value;
		CreateSwapChain();
		OnResize();
	}
}

int D3DApp::run()
{
	//HACCEL hAccelTable = LoadAccelerators(mhAppInst, MAKEINTRESOURCE(IDC_DIRECTX12EXAMPLECODE));
	MSG msg{ 0 };
	mTimer.Reset();

	while (msg.message != WM_QUIT)
	{
		//TranslateAccelerator(msg.hwnd, hAccelTable, &msg)
		if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		else
		{
			mTimer.Tick();

			if (!mAppPaused)
			{
				CalculateFrameStats();
				Update(mTimer);
				Draw(mTimer);
			}
			else
			{
				Sleep(100);
			}
		}
	}
	return static_cast<int>(msg.wParam);
}

bool D3DApp::Initialize()
{
	if (!InitMainWindow())
	{
		return false;
	}
	if (!InitDirect3D())
	{
		return false;
	}

	OnResize();
	return true;
}

void D3DApp::CreateRtvAndDsvDescriptorHeaps()
{
	D3D12_DESCRIPTOR_HEAP_DESC rtvheapDesc;
	rtvheapDesc.NumDescriptors = SwapChainBufferCount;
	rtvheapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
	rtvheapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
	rtvheapDesc.NodeMask = 0;
	ThrowIfFailed(md3dDevice->CreateDescriptorHeap(&rtvheapDesc, IID_PPV_ARGS(mRtvHeap.GetAddressOf())));

	D3D12_DESCRIPTOR_HEAP_DESC dsvHeapDesc;
	dsvHeapDesc.NumDescriptors = 1;
	dsvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
	dsvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
	dsvHeapDesc.NodeMask = 0;
	ThrowIfFailed(md3dDevice->CreateDescriptorHeap(&dsvHeapDesc, IID_PPV_ARGS(mDsvHeap.GetAddressOf())));
}

void D3DApp::OnResize()
{
	assert(md3dDevice);
	assert(mSwapChain);
	assert(mDirectCmdListAlloc);

	FlushCommandQueue();

	ThrowIfFailed(mCommandList->Reset(mDirectCmdListAlloc.Get(), nullptr));
	
	for (int i = 0; i < SwapChainBufferCount; ++i)
	{
		mSwapChainbuffer[i].Reset();
	}
	mDepthStencilBuffer.Reset();

	ThrowIfFailed(mSwapChain->ResizeBuffers(
		SwapChainBufferCount, 
		mClientWidth, 
		mClientHeight, 
		mBackBufferFormat, 
		DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH));

	mCurrentBackBuffer = 0;

	D3D12_CPU_DESCRIPTOR_HANDLE rtvHeapHandle(mRtvHeap->GetCPUDescriptorHandleForHeapStart());
	for (UINT i = 0; i < SwapChainBufferCount; ++i)
	{
		ThrowIfFailed(mSwapChain->GetBuffer(i, IID_PPV_ARGS(&mSwapChainbuffer[i])));
		md3dDevice->CreateRenderTargetView(mSwapChainbuffer[i].Get(), nullptr, rtvHeapHandle);
		rtvHeapHandle.ptr += mRtvDescriptorSize;
	}

	D3D12_RESOURCE_DESC depthStencilDesc;
	depthStencilDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
	depthStencilDesc.Alignment = 0;
	depthStencilDesc.Width = mClientWidth;
	depthStencilDesc.Height = mClientHeight;
	depthStencilDesc.DepthOrArraySize = 1;
	depthStencilDesc.MipLevels = 1;
	
	depthStencilDesc.Format = DXGI_FORMAT_R24G8_TYPELESS;
	depthStencilDesc.SampleDesc.Count = m4xMsaaState ? 4 : 1;
	depthStencilDesc.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
	depthStencilDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
	depthStencilDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;

	D3D12_CLEAR_VALUE optClear;
	optClear.Format = mDepthStencilFormat;
	optClear.DepthStencil.Depth = 1.f;
	optClear.DepthStencil.Stencil = 0;
	
	D3D12_HEAP_PROPERTIES HeapProperty;
	HeapProperty.Type = D3D12_HEAP_TYPE_DEFAULT;
	HeapProperty.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
	HeapProperty.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
	HeapProperty.CreationNodeMask = 1;
	HeapProperty.VisibleNodeMask = 1;

	ThrowIfFailed(md3dDevice->CreateCommittedResource(
		&HeapProperty, D3D12_HEAP_FLAG_NONE, 
		&depthStencilDesc, D3D12_RESOURCE_STATE_COMMON, 
		&optClear, IID_PPV_ARGS(mDepthStencilBuffer.GetAddressOf())));

	D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc;
	dsvDesc.Flags = D3D12_DSV_FLAG_NONE;
	dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
	dsvDesc.Format = mDepthStencilFormat;
	dsvDesc.Texture2D.MipSlice = 0;
	md3dDevice->CreateDepthStencilView(mDepthStencilBuffer.Get(), &dsvDesc, DepthStencilView());

	D3D12_RESOURCE_BARRIER Barrier;
	ZeroMemory(&Barrier, sizeof(Barrier));
	Barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
	Barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
	Barrier.Transition.pResource = mDepthStencilBuffer.Get();
	Barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COMMON;
	Barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_DEPTH_WRITE;
	mCommandList->ResourceBarrier(1, &Barrier);

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

	FlushCommandQueue();

	mScreenViewport.TopLeftX = 0;
	mScreenViewport.TopLeftY = 0;
	mScreenViewport.Width = static_cast<float>(mClientWidth);
	mScreenViewport.Height = static_cast<float>(mClientHeight);
	mScreenViewport.MinDepth = 0.0f;
	mScreenViewport.MaxDepth = 1.0f;

	mScissorRect = { 0, 0, mClientWidth, mClientHeight };
}

LRESULT D3DApp::MsgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	//WndProc
	switch (msg)
	{
	case WM_COMMAND:
	{
		int wmId = LOWORD(wParam);
		// Parse the menu selections:
		switch (wmId)
		{
		case IDM_ABOUT:
			//DialogBox(mhAppInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, About);
			break;
		case ID_EXAMPLE_CHAPTER6:

			break;
		case IDM_EXIT:
			DestroyWindow(hwnd);
			break;
		default:
			return DefWindowProc(hwnd, msg, wParam, lParam);
		}
	}
		break;
	case WM_PAINT:
	{
		PAINTSTRUCT ps;
		HDC hdc = BeginPaint(hwnd, &ps);
		// TODO: Add any drawing code that uses hdc here...
		EndPaint(hwnd, &ps);
	}
	break;
	case WM_ACTIVATE:
		if (LOWORD(wParam) == WA_INACTIVE)
		{
			mAppPaused = true;
			mTimer.Stop();
		}
		else
		{
			mAppPaused = false;
			mTimer.Start();
		}
		return 0;
	case WM_SIZE:
		mClientWidth = LOWORD(lParam);
		mClientHeight = HIWORD(lParam);
		if (md3dDevice)
		{
			if (wParam == SIZE_MINIMIZED)
			{
				mAppPaused = true;
				mMinimized = true;
				mMaximized = false;
			}
			else if (wParam == SIZE_MAXIMIZED)
			{
				mAppPaused = false;
				mMinimized = false;
				mMaximized = true;
				OnResize();
			}
			else if (wParam == SIZE_RESTORED)
			{
				if (mMinimized)
				{
					mAppPaused = false;
					mMinimized = false;
					OnResize();
				}
				else if (mMaximized)
				{
					mAppPaused = false;
					mMaximized = false;
					OnResize();
				}
				else if (mResizing)
				{

				}
				else
				{
					OnResize();
				}
			}
		}
		return 0;
	case WM_ENTERSIZEMOVE:
		mAppPaused = true;
		mResizing = true;
		mTimer.Stop();
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_MENUCHAR:
		return MAKELRESULT(0, MNC_CLOSE);
	case WM_GETMINMAXINFO:
		((MINMAXINFO*)lParam)->ptMinTrackSize.x = 200;
		((MINMAXINFO*)lParam)->ptMinTrackSize.y = 200;
		return 0;
	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;
	case WM_KEYUP:
		if (wParam == VK_ESCAPE)
		{
			PostQuitMessage(0);
		}
		else if (static_cast<int>(wParam) == VK_F2)
		{
			Set4xMsaaState(!m4xMsaaState);
		}
		return 0;
	}
	return DefWindowProc(hwnd, msg, wParam, lParam);
}

bool D3DApp::InitMainWindow()
{
	WNDCLASSEXW wc;
	if (!MyRegisterClass(wc))
	{
		MessageBox(0, L"RegisterClass Failed.", 0, 0);
		return false;
	}
	return InitInstance(SW_SHOW);
}

bool D3DApp::InitDirect3D()
{
#if defined(DEBUG) || defined(_DEBUG)
	{
		ComPtr<ID3D12Debug> debugController;
		ThrowIfFailed(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)));
		debugController->EnableDebugLayer();
	}
#endif

	ThrowIfFailed(CreateDXGIFactory1(IID_PPV_ARGS(&mdxgiFactory)));

	HRESULT hardwareResult = D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_12_1, IID_PPV_ARGS(&md3dDevice));

	if (FAILED(hardwareResult))
	{
		ComPtr<IDXGIAdapter4> pWarpAdapter;
		ThrowIfFailed(mdxgiFactory->EnumWarpAdapter(IID_PPV_ARGS(&pWarpAdapter)));

		ThrowIfFailed(D3D12CreateDevice(pWarpAdapter.Get(), D3D_FEATURE_LEVEL_12_1, IID_PPV_ARGS(&md3dDevice)));
	}

	ThrowIfFailed(md3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&mFence)));

	mRtvDescriptorSize = md3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
	mDsvDescriptorSize = md3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
	mCbvSrvUavDescriptorSize = md3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);

	D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS msQualityLevels;
	msQualityLevels.Format = mBackBufferFormat;
	msQualityLevels.SampleCount = 4;
	msQualityLevels.Flags = D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG_NONE;
	msQualityLevels.NumQualityLevels = 0;
	ThrowIfFailed(md3dDevice->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, &msQualityLevels, sizeof(msQualityLevels)));

	m4xMsaaQuality = msQualityLevels.NumQualityLevels;
	assert(m4xMsaaQuality > 0 && "Unexpected Msaa quality level.");

#ifdef _DEBUG
	LogAdpaters();
#endif
	
	CreateCommandObjects();
	CreateSwapChain();
	CreateRtvAndDsvDescriptorHeaps();

	return true;
}

void D3DApp::CreateCommandObjects()
{
	D3D12_COMMAND_QUEUE_DESC queueDesc = {};
	queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
	queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
	ThrowIfFailed(md3dDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&mCommandQueue)));

	ThrowIfFailed(md3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(mDirectCmdListAlloc.GetAddressOf())));

	ThrowIfFailed(md3dDevice->CreateCommandList(
		0, 
		D3D12_COMMAND_LIST_TYPE_DIRECT, 
		mDirectCmdListAlloc.Get(), 
		nullptr, 
		IID_PPV_ARGS(mCommandList.GetAddressOf())));

	mCommandList->Close();
}

void D3DApp::CreateSwapChain()
{
	mSwapChain.Reset();
	
	DXGI_SWAP_CHAIN_DESC sd;
	sd.BufferDesc.Width = mClientWidth;
	sd.BufferDesc.Height = mClientHeight;
	sd.BufferDesc.RefreshRate.Numerator = 60;
	sd.BufferDesc.RefreshRate.Denominator = 1;
	sd.BufferDesc.Format = mBackBufferFormat;
	sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
	sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
	sd.SampleDesc.Count = m4xMsaaState ? 4 : 1;
	sd.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
	sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	sd.BufferCount = SwapChainBufferCount;
	sd.OutputWindow = mhMainWnd;
	sd.Windowed = true;
	sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
	sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
	

	ThrowIfFailed(mdxgiFactory->CreateSwapChain(mCommandQueue.Get(), &sd, mSwapChain.GetAddressOf()));
	//ThrowIfFailed(mdxgiFactory->CreateSwapChainForHwnd(mCommandQueue.Get(), mhMainWnd, &sd1, &swapChainFSDesc, nullptr, &mSwapChain));
	//not recommand to use CreateSwapChain
}

void D3DApp::FlushCommandQueue()
{
	++mCurrentFence;
	
	ThrowIfFailed(mCommandQueue->Signal(mFence.Get(), mCurrentFence));

	if (mFence->GetCompletedValue() < mCurrentFence)
	{
		HANDLE eventHandle = CreateEvent(nullptr, false, false, nullptr);
		//HANDLE eventHandle = CreateEventEx(nullptr, false, false, EVENT_ALL_ACCESS);
		//https://github.com/microsoft/DirectX-Graphics-Samples/issues/15

		ThrowIfFailed(mFence->SetEventOnCompletion(mCurrentFence, eventHandle));

		WaitForSingleObject(eventHandle, INFINITE);
		CloseHandle(eventHandle);
	}
}

ID3D12Resource* D3DApp::CurrentBackBuffer() const
{
	return mSwapChainbuffer[mCurrentBackBuffer].Get();
}

D3D12_CPU_DESCRIPTOR_HANDLE D3DApp::CurrentBackBufferView() const
{
	D3D12_CPU_DESCRIPTOR_HANDLE result(mRtvHeap->GetCPUDescriptorHandleForHeapStart());
	result.ptr += mCurrentBackBuffer * mRtvDescriptorSize;
	return result;
}

D3D12_CPU_DESCRIPTOR_HANDLE D3DApp::DepthStencilView() const
{
	return mDsvHeap->GetCPUDescriptorHandleForHeapStart();
}

void D3DApp::CalculateFrameStats()
{
	static int frameCnt = 0;
	static float timeElapse = 0.f;

	++frameCnt;

	if ((mTimer.TotalTime() - timeElapse) >= 1.f)
	{
		float fps = static_cast<float>(frameCnt);
		float mspf = 1000.f / fps;
		
		wstring fpsStr = to_wstring(fps);
		wstring mspStr = to_wstring(mspf);

		wstring windowText = mMainWndCaption + L",    fps: " + fpsStr + L"ms,    pf: " + mspStr;

		SetWindowText(mhMainWnd, windowText.c_str());
		frameCnt = 0;
		timeElapse += 1.f;
	}
}

void D3DApp::LogAdpaters()
{
	UINT i = 0;
	IDXGIAdapter* adapter = nullptr;
	std::vector<IDXGIAdapter*> adapterList;
	while (mdxgiFactory->EnumAdapters(i, &adapter) != DXGI_ERROR_NOT_FOUND)
	{
		DXGI_ADAPTER_DESC desc;
		adapter->GetDesc(&desc);

		std::wstring text = L"***Adpater: ";
		text += desc.Description;
		text += L"\n";

		OutputDebugString(text.c_str());

		adapterList.push_back(adapter);

		++i;
	}

	for (size_t i = 0; i < adapterList.size(); ++i)
	{
		LogAdpaterOutputs(adapterList[i]);
		if (adapterList[i])
		{
			adapterList[i]->Release();
			adapterList[i] = 0;
		}
	}
}

void D3DApp::LogAdpaterOutputs(IDXGIAdapter* adapter)
{
	UINT i = 0;
	IDXGIOutput* output = nullptr;
	while (adapter->EnumOutputs(i, &output) != DXGI_ERROR_NOT_FOUND)
	{
		DXGI_OUTPUT_DESC desc;
		output->GetDesc(&desc);

		std::wstring text = L"***Output: ";
		text += desc.DeviceName;
		text += L"\n";
		OutputDebugString(text.c_str());

		LogOutputDisplayModes(output, mBackBufferFormat);

		if (output)
		{
			output->Release();
			output = 0;
		}

		++i;
	}
}

void D3DApp::LogOutputDisplayModes(IDXGIOutput* output, DXGI_FORMAT format)
{
	UINT count = 0;
	UINT flags = 0;

	output->GetDisplayModeList(format, flags, &count, nullptr);

	std::vector<DXGI_MODE_DESC> modeList(count);
	output->GetDisplayModeList(format, flags, &count, &modeList[0]);

	for (const auto& x : modeList)
	{
		UINT n = x.RefreshRate.Numerator;
		UINT d = x.RefreshRate.Denominator;
		std::wstring text =
			L"Width = " + to_wstring(x.Width) + L" " +
			L"Height = " + to_wstring(x.Height) + L" " +
			L"Refresh = " + to_wstring(n) + L"/" + to_wstring(d) + L"\n";

		::OutputDebugString(text.c_str());
	}
}

//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
ATOM D3DApp::MyRegisterClass(WNDCLASSEXW& wc)
{
	wc.cbSize = sizeof(WNDCLASSEX);
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = MainWndProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = mhAppInst;
	wc.hIcon = LoadIcon(mhAppInst, MAKEINTRESOURCE(IDI_DIRECTX12EXAMPLECODE));
	wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wc.lpszMenuName = MAKEINTRESOURCEW(IDC_DIRECTX12EXAMPLECODE);
	wc.lpszClassName = L"MainWnd";
	wc.hIconSm = LoadIcon(wc.hInstance, MAKEINTRESOURCE(IDI_SMALL));
	return RegisterClassExW(&wc);
}

//
//   FUNCTION: InitInstance(HINSTANCE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL D3DApp::InitInstance(int nCmdShow)
{
	RECT R = { 0, 0, mClientWidth, mClientHeight };
	AdjustWindowRect(&R, WS_OVERLAPPEDWINDOW, false);
	int width = R.right - R.left;
	int height = R.bottom - R.top;
	mhMainWnd = CreateWindow(L"MainWnd", mMainWndCaption.c_str(),
		WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, width, height, nullptr, nullptr, mhAppInst, nullptr);
	if (!mhMainWnd)
	{
		MessageBox(0, L"CreateWindow Failed.", 0, 0);
		return false;
	}

	ShowWindow(mhMainWnd, nCmdShow);
	UpdateWindow(mhMainWnd);

	return true;
}

INT_PTR CALLBACK D3DApp::About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);
	switch (message)
	{
	case WM_INITDIALOG:
		return (INT_PTR)TRUE;

	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
		{
			EndDialog(hDlg, LOWORD(wParam));
			return (INT_PTR)TRUE;
		}
		break;
	}
	return (INT_PTR)FALSE;
}

 

선언부에 앞서 header나 namespace 먼저 간단하게 짚겠습니다.

#include "D3DApp.h"
#include <cassert>

using namespace std;
using namespace Microsoft::WRL;

cassert는 c언어에서 쓰는 assert. 즉, 값을 확인하는 기능을 사용하기 위해 선언 된 것입니다.

기본적인 string 연산이 log에서 사용되므로 std namespace가 선언되어 있고, 

DirectX 기능들을 담당하는 Class들은 Microsoft::WRL로 묶여 있어 이 namespace가 선언되어 있습니다.

 

다음 구현부를 보겠습니다.

LRESULT CALLBACK
MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	return D3DApp::GetApp()->MsgProc(hwnd, msg, wParam, lParam);
}

D3DApp* D3DApp::mApp = nullptr;
D3DApp* D3DApp::GetApp()
{
	return mApp;
}

D3DApp::D3DApp(HINSTANCE hInstance) : mhAppInst(hInstance)
{
	assert(mApp == nullptr);
	mApp = this;
}

D3DApp::~D3DApp()
{
	if (md3dDevice != nullptr)
	{
		FlushCommandQueue();
	}
}

HINSTANCE D3DApp::AppInst() const
{
	return mhAppInst;
}

HWND D3DApp::MainWnd() const
{
	return mhMainWnd;
}

float D3DApp::AspectRatio() const
{
	return static_cast<float>(mClientWidth) / mClientHeight;
}

bool D3DApp::Get4xMsaaState() const
{
	return m4xMsaaState;
}

void D3DApp::Set4xMsaaState(bool value)
{
	if (m4xMsaaState != value)
	{
		m4xMsaaState = value;
		CreateSwapChain();
		OnResize();
	}
}

최상단의 MainWndProc는 MainWnd라는 Window Programming Main에서의 Callback 함수입니다.

아마 나중에 분리가 될 것 같습니다. 자세한 것은 Window Programming을 공부합시다.

 

이 안에서 Static 변수인 mApp도 초기화가 되고, 생성자와 소멸자가 선언되어 있습니다.

생성자에서는 mApp이 존재하느냐를 확인하고, 존재하면 이를 자기 자신 객체로 담습니다.

전형적인 비 Multi Thread에서의 Singleton 방식 중 하나이죠.

 

소멸자에서는 CommandQueue를 모두 비워줍니다.

그 외에는 대부분 Getter이거나, 간단한 연산만 진행됩니다.

Set4xMsaaState은 4xMsaa를 켰다가 껐을 때 SwapChain을 새로 생성하고 Resize를 실행하는 것입니다.

 

여기 있는 함수들은 생성자와 소멸자는 Singleton을 없애고, 나머지는 모두 분리될 것입니다.

 

다음은 DirectX의 실행을 담당하는 함수들의 구현체입니다.

int D3DApp::run()
{
	MSG msg{ 0 };
	mTimer.Reset();

	while (msg.message != WM_QUIT)
	{
		if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		else
		{
			mTimer.Tick();

			if (!mAppPaused)
			{
				CalculateFrameStats();
				Update(mTimer);
				Draw(mTimer);
			}
			else
			{
				Sleep(100);
			}
		}
	}
	return static_cast<int>(msg.wParam);
}

bool D3DApp::Initialize()
{
	if (!InitMainWindow())
	{
		return false;
	}
	if (!InitDirect3D())
	{
		return false;
	}

	OnResize();
	return true;
}

void D3DApp::CreateRtvAndDsvDescriptorHeaps()
{
	D3D12_DESCRIPTOR_HEAP_DESC rtvheapDesc;
	rtvheapDesc.NumDescriptors = SwapChainBufferCount;
	rtvheapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
	rtvheapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
	rtvheapDesc.NodeMask = 0;
	ThrowIfFailed(md3dDevice->CreateDescriptorHeap(&rtvheapDesc, IID_PPV_ARGS(mRtvHeap.GetAddressOf())));

	D3D12_DESCRIPTOR_HEAP_DESC dsvHeapDesc;
	dsvHeapDesc.NumDescriptors = 1;
	dsvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
	dsvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
	dsvHeapDesc.NodeMask = 0;
	ThrowIfFailed(md3dDevice->CreateDescriptorHeap(&dsvHeapDesc, IID_PPV_ARGS(mDsvHeap.GetAddressOf())));
}

run은 Model을 실질적으로 실행시키는 함수입니다.

이 부분도 여러 부분이 섞여 있는데, DirectX에서 다룰 부분을 최소화로 줄여보자면 else 안 부분만 포함 될 것입니다.

때문에 이 부분도 나중에 수정을 해줄 예정입니다.

 

Initialize 역시 Window 초기화와 DirectX 초기화가 섞여있기에 차후 수정 될 예정입니다.

 

CreateRtvAndDsvDescriptorHeaps는 Renter Target View, Depth Stencil View의 Descriptor Heap을 생성합니다.

Descriptor Heap은 반드시 초기화 과정에서 생성 되어야 하기 때문에 이 점을 유의함녀 됩니다.

 

이 뒤는 Windows 부분 기능과 좀 더 가까운 구현체들입니다.

void D3DApp::OnResize()
{
	assert(md3dDevice);
	assert(mSwapChain);
	assert(mDirectCmdListAlloc);

	FlushCommandQueue();

	ThrowIfFailed(mCommandList->Reset(mDirectCmdListAlloc.Get(), nullptr));
	
	for (int i = 0; i < SwapChainBufferCount; ++i)
	{
		mSwapChainbuffer[i].Reset();
	}
	mDepthStencilBuffer.Reset();

	ThrowIfFailed(mSwapChain->ResizeBuffers(
		SwapChainBufferCount, 
		mClientWidth, 
		mClientHeight, 
		mBackBufferFormat, 
		DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH));

	mCurrentBackBuffer = 0;

	D3D12_CPU_DESCRIPTOR_HANDLE rtvHeapHandle(mRtvHeap->GetCPUDescriptorHandleForHeapStart());
	for (UINT i = 0; i < SwapChainBufferCount; ++i)
	{
		ThrowIfFailed(mSwapChain->GetBuffer(i, IID_PPV_ARGS(&mSwapChainbuffer[i])));
		md3dDevice->CreateRenderTargetView(mSwapChainbuffer[i].Get(), nullptr, rtvHeapHandle);
		rtvHeapHandle.ptr += mRtvDescriptorSize;
	}

	D3D12_RESOURCE_DESC depthStencilDesc;
	depthStencilDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
	depthStencilDesc.Alignment = 0;
	depthStencilDesc.Width = mClientWidth;
	depthStencilDesc.Height = mClientHeight;
	depthStencilDesc.DepthOrArraySize = 1;
	depthStencilDesc.MipLevels = 1;
	
	depthStencilDesc.Format = DXGI_FORMAT_R24G8_TYPELESS;
	depthStencilDesc.SampleDesc.Count = m4xMsaaState ? 4 : 1;
	depthStencilDesc.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
	depthStencilDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
	depthStencilDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;

	D3D12_CLEAR_VALUE optClear;
	optClear.Format = mDepthStencilFormat;
	optClear.DepthStencil.Depth = 1.f;
	optClear.DepthStencil.Stencil = 0;
	
	D3D12_HEAP_PROPERTIES HeapProperty;
	HeapProperty.Type = D3D12_HEAP_TYPE_DEFAULT;
	HeapProperty.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
	HeapProperty.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
	HeapProperty.CreationNodeMask = 1;
	HeapProperty.VisibleNodeMask = 1;

	ThrowIfFailed(md3dDevice->CreateCommittedResource(
		&HeapProperty, D3D12_HEAP_FLAG_NONE, 
		&depthStencilDesc, D3D12_RESOURCE_STATE_COMMON, 
		&optClear, IID_PPV_ARGS(mDepthStencilBuffer.GetAddressOf())));

	D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc;
	dsvDesc.Flags = D3D12_DSV_FLAG_NONE;
	dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
	dsvDesc.Format = mDepthStencilFormat;
	dsvDesc.Texture2D.MipSlice = 0;
	md3dDevice->CreateDepthStencilView(mDepthStencilBuffer.Get(), &dsvDesc, DepthStencilView());

	D3D12_RESOURCE_BARRIER Barrier;
	ZeroMemory(&Barrier, sizeof(Barrier));
	Barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
	Barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
	Barrier.Transition.pResource = mDepthStencilBuffer.Get();
	Barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COMMON;
	Barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_DEPTH_WRITE;
	mCommandList->ResourceBarrier(1, &Barrier);

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

	FlushCommandQueue();

	mScreenViewport.TopLeftX = 0;
	mScreenViewport.TopLeftY = 0;
	mScreenViewport.Width = static_cast<float>(mClientWidth);
	mScreenViewport.Height = static_cast<float>(mClientHeight);
	mScreenViewport.MinDepth = 0.0f;
	mScreenViewport.MaxDepth = 1.0f;

	mScissorRect = { 0, 0, mClientWidth, mClientHeight };
}

LRESULT D3DApp::MsgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	//WndProc
	switch (msg)
	{
	case WM_COMMAND:
	{
		int wmId = LOWORD(wParam);
		// Parse the menu selections:
		switch (wmId)
		{
		case IDM_ABOUT:
			//DialogBox(mhAppInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, About);
			break;
		case ID_EXAMPLE_CHAPTER6:

			break;
		case IDM_EXIT:
			DestroyWindow(hwnd);
			break;
		default:
			return DefWindowProc(hwnd, msg, wParam, lParam);
		}
	}
		break;
	case WM_PAINT:
	{
		PAINTSTRUCT ps;
		HDC hdc = BeginPaint(hwnd, &ps);
		// TODO: Add any drawing code that uses hdc here...
		EndPaint(hwnd, &ps);
	}
	break;
	case WM_ACTIVATE:
		if (LOWORD(wParam) == WA_INACTIVE)
		{
			mAppPaused = true;
			mTimer.Stop();
		}
		else
		{
			mAppPaused = false;
			mTimer.Start();
		}
		return 0;
	case WM_SIZE:
		mClientWidth = LOWORD(lParam);
		mClientHeight = HIWORD(lParam);
		if (md3dDevice)
		{
			if (wParam == SIZE_MINIMIZED)
			{
				mAppPaused = true;
				mMinimized = true;
				mMaximized = false;
			}
			else if (wParam == SIZE_MAXIMIZED)
			{
				mAppPaused = false;
				mMinimized = false;
				mMaximized = true;
				OnResize();
			}
			else if (wParam == SIZE_RESTORED)
			{
				if (mMinimized)
				{
					mAppPaused = false;
					mMinimized = false;
					OnResize();
				}
				else if (mMaximized)
				{
					mAppPaused = false;
					mMaximized = false;
					OnResize();
				}
				else if (mResizing)
				{

				}
				else
				{
					OnResize();
				}
			}
		}
		return 0;
	case WM_ENTERSIZEMOVE:
		mAppPaused = true;
		mResizing = true;
		mTimer.Stop();
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_MENUCHAR:
		return MAKELRESULT(0, MNC_CLOSE);
	case WM_GETMINMAXINFO:
		((MINMAXINFO*)lParam)->ptMinTrackSize.x = 200;
		((MINMAXINFO*)lParam)->ptMinTrackSize.y = 200;
		return 0;
	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;
	case WM_KEYUP:
		if (wParam == VK_ESCAPE)
		{
			PostQuitMessage(0);
		}
		else if (static_cast<int>(wParam) == VK_F2)
		{
			Set4xMsaaState(!m4xMsaaState);
		}
		return 0;
	}
	return DefWindowProc(hwnd, msg, wParam, lParam);
}

bool D3DApp::InitMainWindow()
{
	WNDCLASSEXW wc;
	if (!MyRegisterClass(wc))
	{
		MessageBox(0, L"RegisterClass Failed.", 0, 0);
		return false;
	}
	return InitInstance(SW_SHOW);
}

어떻게 보면 가장 긴 함수들입니다.

우선 정리를 하면, MsgProc는 Window에서의 이벤트 함수를 연결하는 함수입니다.

무시하면 됩니다.

InitMainWindow는 Window의 초기화 함수입니다.

이 또한 이 기능이 바뀔 일이 거의 없으며, 그대로 분리 될 예정이니 무시하면 됩니다.

 

우리가 볼 것은 OnResize입니다.

OnResize는 화면 크기가 바뀔 때 호출이 됩니다.

이는 매우 간단하게 생각할 문제가 아닙니다.

여태까지 담겨 있던 Command, 모든 View Descriptor, Swap Chain 값들이 모두 바뀌어야 하기 때문입니다.

즉, 모든 것이 flush 되고 reset 되고 다시 생성이 되어야 합니다.

 

그렇기에 assert로 값을 보장하고, Flush 하거나 Reset을 해줍니다.

그 뒤 SwapChain의 크기를 새 화면 크기로 변경하고, RTV, DSV를 새로 생성해준 뒤 Barrier로 마무리한다.

 

사실 이렇게 긴 이유는 MS가 지원해주는 구현체를 사용하지 않기 때문이다.

이를 사용한다면 40줄은 더 줄일 수 있을 것이다.

 

다음 구현체는 DirectX 기능 위주의 구현체이다.

bool D3DApp::InitDirect3D()
{
#if defined(DEBUG) || defined(_DEBUG)
	{
		ComPtr<ID3D12Debug> debugController;
		ThrowIfFailed(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)));
		debugController->EnableDebugLayer();
	}
#endif

	ThrowIfFailed(CreateDXGIFactory1(IID_PPV_ARGS(&mdxgiFactory)));

	HRESULT hardwareResult = D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_12_1, IID_PPV_ARGS(&md3dDevice));

	if (FAILED(hardwareResult))
	{
		ComPtr<IDXGIAdapter4> pWarpAdapter;
		ThrowIfFailed(mdxgiFactory->EnumWarpAdapter(IID_PPV_ARGS(&pWarpAdapter)));

		ThrowIfFailed(D3D12CreateDevice(pWarpAdapter.Get(), D3D_FEATURE_LEVEL_12_1, IID_PPV_ARGS(&md3dDevice)));
	}

	ThrowIfFailed(md3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&mFence)));

	mRtvDescriptorSize = md3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
	mDsvDescriptorSize = md3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
	mCbvSrvUavDescriptorSize = md3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);

	D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS msQualityLevels;
	msQualityLevels.Format = mBackBufferFormat;
	msQualityLevels.SampleCount = 4;
	msQualityLevels.Flags = D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG_NONE;
	msQualityLevels.NumQualityLevels = 0;
	ThrowIfFailed(md3dDevice->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, &msQualityLevels, sizeof(msQualityLevels)));

	m4xMsaaQuality = msQualityLevels.NumQualityLevels;
	assert(m4xMsaaQuality > 0 && "Unexpected Msaa quality level.");

#ifdef _DEBUG
	LogAdpaters();
#endif
	
	CreateCommandObjects();
	CreateSwapChain();
	CreateRtvAndDsvDescriptorHeaps();

	return true;
}

void D3DApp::CreateCommandObjects()
{
	D3D12_COMMAND_QUEUE_DESC queueDesc = {};
	queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
	queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
	ThrowIfFailed(md3dDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&mCommandQueue)));

	ThrowIfFailed(md3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(mDirectCmdListAlloc.GetAddressOf())));

	ThrowIfFailed(md3dDevice->CreateCommandList(
		0, 
		D3D12_COMMAND_LIST_TYPE_DIRECT, 
		mDirectCmdListAlloc.Get(), 
		nullptr, 
		IID_PPV_ARGS(mCommandList.GetAddressOf())));

	mCommandList->Close();
}

void D3DApp::CreateSwapChain()
{
	mSwapChain.Reset();
	
	DXGI_SWAP_CHAIN_DESC sd;
	sd.BufferDesc.Width = mClientWidth;
	sd.BufferDesc.Height = mClientHeight;
	sd.BufferDesc.RefreshRate.Numerator = 60;
	sd.BufferDesc.RefreshRate.Denominator = 1;
	sd.BufferDesc.Format = mBackBufferFormat;
	sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
	sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
	sd.SampleDesc.Count = m4xMsaaState ? 4 : 1;
	sd.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
	sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	sd.BufferCount = SwapChainBufferCount;
	sd.OutputWindow = mhMainWnd;
	sd.Windowed = true;
	sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
	sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
	

	ThrowIfFailed(mdxgiFactory->CreateSwapChain(mCommandQueue.Get(), &sd, mSwapChain.GetAddressOf()));
	//ThrowIfFailed(mdxgiFactory->CreateSwapChainForHwnd(mCommandQueue.Get(), mhMainWnd, &sd1, &swapChainFSDesc, nullptr, &mSwapChain));
	//not recommand to use CreateSwapChain
}

void D3DApp::FlushCommandQueue()
{
	++mCurrentFence;
	
	ThrowIfFailed(mCommandQueue->Signal(mFence.Get(), mCurrentFence));

	if (mFence->GetCompletedValue() < mCurrentFence)
	{
		HANDLE eventHandle = CreateEvent(nullptr, false, false, nullptr);
		//HANDLE eventHandle = CreateEventEx(nullptr, false, false, EVENT_ALL_ACCESS);
		//https://github.com/microsoft/DirectX-Graphics-Samples/issues/15

		ThrowIfFailed(mFence->SetEventOnCompletion(mCurrentFence, eventHandle));

		WaitForSingleObject(eventHandle, INFINITE);
		CloseHandle(eventHandle);
	}
}

InitDirect3D는 DirectX를 사용하기 위한 준비를 하는 함수이다.

우선 DirectX 기능을 사용하기 위한 기본 객체인 D3D12Device를 생성하고, 값을 검증 한 뒤 이를 기반으로 RTV, DSV,

CBVSRVUAV (Constant Buffer View, Shader Resource View, Unordered Access View) Descriptor Heap Size를 저장한다.

그리고 QualityLevel을 설정하고, 4xMsaaQuality를 설정한다.

마지막으로 CommandObject, SwapChain, DescriptorHeaps를 생성한다.

이 중 Descriptorheaps 생성 함수는 위에서 보았고, 먼저 CreateCommandObject부터 보자.

 

CreateCommandObject에서는 CommandQueue를 생성해 그 속성을 지정하고, CommandQueue를 생성한다.

그 다음 비어있는 Allocator를 생성해 앞서 생성한 CommandQueue와 속성을 동일하게 맞추고, 

CommandList를 생성하면서 Allocator와 CommandList를 연결한다.

그리고 CommandList를 닫아서 수정 단계를 끝낸다.

 

CreateSwapChain은 더 간단하다.

기존의 SwapChain을 모두 비워주고,

필요한 설정을 구조체에 채워넣고 CreateSwapChain을 선언해서 새로운 SwapChain을 생성한다.

 

마지막으로 FlushCommandQueue는 솔직히 어떻게 움직이는지 잘 모르겠습니다.

나중에 이해가 가면 수정을 하겠습니다.

 

위에서 핵심적인 코드를 주로 살펴보았으니 비교적 덜 코어한 코드를 살펴봅시다.

ID3D12Resource* D3DApp::CurrentBackBuffer() const
{
	return mSwapChainbuffer[mCurrentBackBuffer].Get();
}

D3D12_CPU_DESCRIPTOR_HANDLE D3DApp::CurrentBackBufferView() const
{
	D3D12_CPU_DESCRIPTOR_HANDLE result(mRtvHeap->GetCPUDescriptorHandleForHeapStart());
	result.ptr += mCurrentBackBuffer * mRtvDescriptorSize;
	return result;
}

D3D12_CPU_DESCRIPTOR_HANDLE D3DApp::DepthStencilView() const
{
	return mDsvHeap->GetCPUDescriptorHandleForHeapStart();
}

void D3DApp::CalculateFrameStats()
{
	static int frameCnt = 0;
	static float timeElapse = 0.f;

	++frameCnt;

	if ((mTimer.TotalTime() - timeElapse) >= 1.f)
	{
		float fps = static_cast<float>(frameCnt);
		float mspf = 1000.f / fps;
		
		wstring fpsStr = to_wstring(fps);
		wstring mspStr = to_wstring(mspf);

		wstring windowText = mMainWndCaption + L",    fps: " + fpsStr + L"ms,    pf: " + mspStr;

		SetWindowText(mhMainWnd, windowText.c_str());
		frameCnt = 0;
		timeElapse += 1.f;
	}
}

void D3DApp::LogAdpaters()
{
	UINT i = 0;
	IDXGIAdapter* adapter = nullptr;
	std::vector<IDXGIAdapter*> adapterList;
	while (mdxgiFactory->EnumAdapters(i, &adapter) != DXGI_ERROR_NOT_FOUND)
	{
		DXGI_ADAPTER_DESC desc;
		adapter->GetDesc(&desc);

		std::wstring text = L"***Adpater: ";
		text += desc.Description;
		text += L"\n";

		OutputDebugString(text.c_str());

		adapterList.push_back(adapter);

		++i;
	}

	for (size_t i = 0; i < adapterList.size(); ++i)
	{
		LogAdpaterOutputs(adapterList[i]);
		if (adapterList[i])
		{
			adapterList[i]->Release();
			adapterList[i] = 0;
		}
	}
}

void D3DApp::LogAdpaterOutputs(IDXGIAdapter* adapter)
{
	UINT i = 0;
	IDXGIOutput* output = nullptr;
	while (adapter->EnumOutputs(i, &output) != DXGI_ERROR_NOT_FOUND)
	{
		DXGI_OUTPUT_DESC desc;
		output->GetDesc(&desc);

		std::wstring text = L"***Output: ";
		text += desc.DeviceName;
		text += L"\n";
		OutputDebugString(text.c_str());

		LogOutputDisplayModes(output, mBackBufferFormat);

		if (output)
		{
			output->Release();
			output = 0;
		}

		++i;
	}
}

void D3DApp::LogOutputDisplayModes(IDXGIOutput* output, DXGI_FORMAT format)
{
	UINT count = 0;
	UINT flags = 0;

	output->GetDisplayModeList(format, flags, &count, nullptr);

	std::vector<DXGI_MODE_DESC> modeList(count);
	output->GetDisplayModeList(format, flags, &count, &modeList[0]);

	for (const auto& x : modeList)
	{
		UINT n = x.RefreshRate.Numerator;
		UINT d = x.RefreshRate.Denominator;
		std::wstring text =
			L"Width = " + to_wstring(x.Width) + L" " +
			L"Height = " + to_wstring(x.Height) + L" " +
			L"Refresh = " + to_wstring(n) + L"/" + to_wstring(d) + L"\n";

		::OutputDebugString(text.c_str());
	}
}

단순 연산이나 Getter, 혹은 Log를 출력하는 함수들입니다.

그냥 그렇다 하고 넘어갑시다.

 

마지막 대미를 장식하는건 분리되어야 할 Window 관련 함수입니다.

//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
ATOM D3DApp::MyRegisterClass(WNDCLASSEXW& wc)
{
	wc.cbSize = sizeof(WNDCLASSEX);
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = MainWndProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = mhAppInst;
	wc.hIcon = LoadIcon(mhAppInst, MAKEINTRESOURCE(IDI_DIRECTX12EXAMPLECODE));
	wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wc.lpszMenuName = MAKEINTRESOURCEW(IDC_DIRECTX12EXAMPLECODE);
	wc.lpszClassName = L"MainWnd";
	wc.hIconSm = LoadIcon(wc.hInstance, MAKEINTRESOURCE(IDI_SMALL));
	return RegisterClassExW(&wc);
}

//
//   FUNCTION: InitInstance(HINSTANCE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL D3DApp::InitInstance(int nCmdShow)
{
	RECT R = { 0, 0, mClientWidth, mClientHeight };
	AdjustWindowRect(&R, WS_OVERLAPPEDWINDOW, false);
	int width = R.right - R.left;
	int height = R.bottom - R.top;
	mhMainWnd = CreateWindow(L"MainWnd", mMainWndCaption.c_str(),
		WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, width, height, nullptr, nullptr, mhAppInst, nullptr);
	if (!mhMainWnd)
	{
		MessageBox(0, L"CreateWindow Failed.", 0, 0);
		return false;
	}

	ShowWindow(mhMainWnd, nCmdShow);
	UpdateWindow(mhMainWnd);

	return true;
}

INT_PTR CALLBACK D3DApp::About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);
	switch (message)
	{
	case WM_INITDIALOG:
		return (INT_PTR)TRUE;

	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
		{
			EndDialog(hDlg, LOWORD(wParam));
			return (INT_PTR)TRUE;
		}
		break;
	}
	return (INT_PTR)FALSE;
}

바뀔 일 없고, 수정 시 분리 될 기능들입니다.

DirectX와 직접 관련이 있는것은 아니니 넘어가도 되지 않나 싶습니다.

 

코드 양에 비해 설명이 적을 수 있습니다.

하지만 다음에 적을 글과 이어진다면 대략적인 흐름은 파악 할 수 있을 것입니다.

 

다음에 작성할 때에는 BoxApp과 Main 함수를 작성하겠습니다.

'내용정리 > DirectX12' 카테고리의 다른 글

20.09.18 개발일지  (0) 2020.09.18
10. Box Example 3 - BoxApp, WinMain  (0) 2020.09.15
10. Box Example 1  (0) 2020.09.04
20.08.25 개발일지  (0) 2020.08.25
20.08.21 개발일지  (0) 2020.08.21

Q: Quaternion(쿼터니언)에 대해 설명하시오.

A: 사원수, 혹은 해밀턴 수라고도 부르며 복소수를 확장해 만든 수 체계이다.

덧셈에 대해서는 결합법칙, 교환법칙이 성립하지만, 곱셈에 대해서는 결합법칙만 성립하고 교환법칙은 성립하지 않는다.

쿼터니언은 하나의 실수와 3개의 서로 다른 복소수로 구성이 되며, 곱셈 연산 시 겹선형 연산을 한다.

예를 들어, a0 + a1i + a2j + a3k에 대해 곱의 결과는 다음과 같이 나타난다.

× 1 i j k
1 1 i j k
i i -1 k -j
j j k -1 -i
k k -j -i -1

단, 여기서 a0, a1, a2, a3는 모두 실수이다.

 

쿼터니언은 실수부와 허수부로 나눌 수 있는데,

이를 벡터에 적용하면 실수부를 스칼라 값으로, 허수부를 3차원 벡터로 간주한다.

 

마지막으로 위 예시에서 언급한 쿼터니언을 2×2 Complex matrix(복소행렬)로 나타내면 다음과 같다.

a + bi c + di
-c + di a - bi

 

Q: 물체의 회전을 연산 할 때 쿼터니언을 사용해야 하는 이유를 설명하시오

A: 오일러 각을 이용해 행렬 연산을 할 때 회전하는 축이 정지한 축과 겹쳐지는 상황이 발생하는데, 

이를 짐벌락이라 부른다.

짐벌락은 오일러 각을 연산 할 때 3개의 축을 기반으로 연산을 하기 때문인데,

이를 회피하기 위해 쿼터니언을 이용한다.

쿼터니언을 이용해서 문제가 해결되는 이유는

3개의 축의 연산이 서로 다른 복소수로 되어 있기 때문에 서로가 독립적으로 연산이 될 수 있다.

 

참고자료

ko.wikipedia.org/wiki/%EC%98%A4%EC%9D%BC%EB%9F%AC_%EA%B0%81

 

오일러 각 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 오일러 각(Euler角, Euler angle)은 강체가 놓인 방향을 3차원 공간에 표시하기 위해 레온하르트 오일러가 도입한 세 개의 각도이다.[1] 즉, 3차원 회전군 SO(3)의 한 좌

ko.wikipedia.org

ko.wikipedia.org/wiki/%EC%82%AC%EC%9B%90%EC%88%98

 

사원수 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 브로엄 다리에 새겨진 기념비. 이 곳에서 해밀턴이 사원수를 발견하였다고 한다. 수학에서, 사원수(四元數, 영어: quaternion 쿼터니언[*]) 또는 해밀턴 수(영어: Ham

ko.wikipedia.org

 

오늘은 Character 코드를 열어서 Network, Visibility, Collision 관련 변수들을 모두 설정해보기도 했고, 

Activate 함수만 Replicate 했으나 해결하지 못했습니다.

역대 최장 이슈인것 같은데 면접이 남아 있어서 집중을 잘 못하고 있습니다.

 

오늘은 새로운 시도를 하고 있는데, Branch를 새로 파서 State 관리를 Trap에서 해볼 생각입니다.

이전까지는 Trap의 생성자에서 Child Actor들의 Initialize를 실행하고, Activate를 실행했습니다.

그럼 각 Object들의 Initialize, Activate에서 Visibility와 Collision을 조절하는데,

사실 이 방식이 Character에서만 잘 안되는 것이지 다른 곳에서는 잘만 됩니다.

 

하지만 이런 조정을 Object에서 이루어지도록 하니까 Character는 잘못 작동하는게 아닌가 싶기도 합니다.

그래서 Visibility는 ChildActor에다가, Collision은 내부 함수 호출을 통해 변경하려 합니다.

일단 다듬어지지 못한 부분이 조금 있는데 당분간은 이 방식으로 좀 고민 해보려 합니다.

 

뭔가 정신이 없어서 말이 이상하게 적히는 것 같은데 정리를 하면

1. 여태까지는 Actor가 스스로 State를 조절했다.

2. 이제는 Child Actor에 이를 지정한다.

이건 Child Actor의 Replicate와 비슷하게 보면 될 것 같습니다.

Replicate Actor에서 Child Component는 Replicated를 설정하지만,

그 안의 Actor는 이를 해제해야 부하가 덜 걸린다고 알고 있습니다.

이처럼 Collision과 Visibility도 Child Component 단위에서 이루어지도록 해보려 합니다.

 

금주 목, 금이 면접이고 빠르면 다음주에 결과가 나오는건데, 그 전에 이 이슈라도 해결했으면 좋겠습니다.

'개발일지 > Treasure Hunter' 카테고리의 다른 글

20.09.14 개발일지  (0) 2020.09.14
20.09.09 개발일지  (0) 2020.09.09
20.09.05 개발일지  (0) 2020.09.05
20.09.02 개발일지  (0) 2020.09.02
20.08.21 개발일지  (0) 2020.08.31

일단 Character에서 Update 함수를 Replicate 함수로 바꿨을 때에는 Client에서도 정상 작동을 하지 않았습니다.

그래서 질문을 올리기 위해 미리 글을 작성하는 도중, 한가지 놓친 부분이 떠올랐습니다.

Character의 상태 변화는 Update 함수이나, 이게 호출되는 것은 Activate때입니다.

그래서 반대로 Replicate 함수를 통해 Activate 때에만 Replicate 함수를 호출해보려 합니다.

이래도 안된다면 이제 정말로 질문글을 올려볼 생각입니다.

해결 방안도 묻고,

결국 dedicate server로 교체할 텐데 사실상 서버인 Host에서의 문제를 무시해도 되는지 물어볼 계획입니다.

'개발일지 > Treasure Hunter' 카테고리의 다른 글

20.09.09 개발일지  (0) 2020.09.09
20.09.07 개발일지  (0) 2020.09.07
20.09.02 개발일지  (0) 2020.09.02
20.08.21 개발일지  (0) 2020.08.31
20.08.27 개발일지  (0) 2020.08.27

이번에는 Chapter 6의 Box Example을

VS에서 제공하는 Windows Programming Template에 적용한 코드를 소개하려 합니다.

Project Introduction

 

Box Example Project

Common 폴더 안의 코드들은 DirectX를 사용하는데 필요한 몇가지 코드들을 모아놓은 것입니다.

즉, 책에서 작성되고, 사용된 코드들의 집합체라는 것입니다.

 

실질적인 구현체인 BoxApp은 Example 폴더 안에 있습니다.

앞으로 추가적인 예시 코드들은 Example 폴더 안에 작성될 예정입니다.

 

폴더에 들어있지 않은 코드 중 DirectX12ExampleCode는 Main 함수가 적혀있습니다.

이 외에는 Window Programming Template에서 제공된 Header들로,

여기에 예시 작성에 필요한 헤더나 선언들을 몇가지 추가 했습니다.

 

마지막으로 ReosurceFiles은 마찬가지로 Template에서 제공된 것들입니다.

앞으로도 신경쓰지 않으면 됩니다.

 

targetver.h

#pragma once

// Including SDKDDKVer.h defines the highest available Windows platform.
// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
#include <SDKDDKVer.h>

항목을 따로 두긴 했지만, 설명에 적힌것과 마찬가지로 Windows platform 버전을 지정하는 것입니다.

앞으로도 이 버전은 따로 건드릴 예정이 없으니 그대로 방치하면 됩니다.

resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by DirectX12ExampleCode.rc
//
#define IDC_MYICON                      2
#define IDD_DIRECTX12EXAMPLECODE_DIALOG 102
#define IDS_APP_TITLE                   103
#define IDD_ABOUTBOX                    103
#define IDM_ABOUT                       104
#define IDM_EXIT                        105
#define IDI_DIRECTX12EXAMPLECODE        107
#define IDI_SMALL                       108
#define IDC_DIRECTX12EXAMPLECODE        109
#define IDR_MAINFRAME                   128
#define ID_EXAMPLE_CHAPTER6             32771
#define IDC_STATIC                      -1

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NO_MFC                     1
#define _APS_NEXT_RESOURCE_VALUE        129
#define _APS_NEXT_COMMAND_VALUE         32774
#define _APS_NEXT_CONTROL_VALUE         1000
#define _APS_NEXT_SYMED_VALUE           110
#endif
#endif

Window에 사용되는 Resource들에 대한 키 값이 지정되어 있는 곳입니다.

여기서 눈여겨 봐야 할 점이라면 ID_EXAMPLE_CHAPTER6입니다.

이는 Example 메뉴에서 Chapter 6를 클릭했을 시 Box Example이 출력되게 하기 위해 추가된 것입니다.

다만 아직 이 기능까지 구현을 하지 못했고, 구조상 아직은 런타임상에서 교체를 할 수 없습니다.

그러니 예시가 늘어날 때마다 새로운 항목만 추가를 합시다.

framework.h

// header.h : include file for standard system include files,
// or project specific include files
//

#pragma once

#include "targetver.h"
#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
// Windows Header Files
#include <windows.h>
// C RunTime Header Files
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>

Template상에서 필요한 헤더들이 선언된 곳입니다.

그즉슨, Window 제작에 필요한 것들이 선언되었다는 것입니다.

아직 완벽히 작성되지 않았지만, 앞으로 Window 제작에 필요한 헤더는 다 이곳으로 이동될 것입니다.

하지만 틀만 완성되면 그 이후로는 DirectX 구현이라 이부분은 크게 게의치 않아도 될 것 같습니다.

 

Common

DXException

#pragma once
#include <Windows.h>
#include <string>

class DXException
{
public:
	DXException();
	DXException(HRESULT hr, const std::wstring& functionName, const std::wstring& filename, int lineNumber);

	std::wstring ToString() const;

	HRESULT ErrorCode = S_OK;
	std::wstring FunctionName;
	std::wstring FileName;
	int LineNumber = -1;
};
#include <comdef.h>
#include "DXException.h"

DXException::DXException(HRESULT hr, const std::wstring& functionName, const std::wstring& filename, int lineNumber)
    : ErrorCode(hr), FunctionName(functionName), FileName(filename), LineNumber(lineNumber)
{

}

std::wstring DXException::ToString() const
{
    _com_error err(ErrorCode);
    std::wstring msg = err.ErrorMessage();

    return FunctionName + L" failed in " + FileName + L"; line " + std::to_wstring(LineNumber) + L"; error: " + msg;
}

DXException은 DirectX Example 작성에 있어서 발생하는 에러를 새로운 Window에 출력하는 역할을 한다.

이는 따로 제공하는 코드는 아니고, 책에서 따로 작성한 코드이다.

이는 HRESULT 값을 통해 오류가 발생한 함수 명, 호출된 파일 명, 줄 번호를 담고 있다.

 

여기서 comdef.h라는 낯선 헤더가 눈에 띌 것이다.

이는 ToString에서 사용하는 _com_error 클래스를 담고 있으며, COM types에서 error type을 정의한다.

https://docs.microsoft.com/ko-kr/cpp/cpp/compiler-com-support-classes?view=vs-2019

 

컴파일러 COM 지원 클래스

 

docs.microsoft.com

GameTimer

#ifndef GAMETIMER_H
#define GAMETIMER_H

class GameTimer
{
public:
	GameTimer();

	float TotalTime()const; // in seconds
	float DeltaTime()const; // in seconds

	void Reset(); // Call before message loop.
	void Start(); // Call when unpaused.
	void Stop();  // Call when paused.
	void Tick();  // Call every frame.

private:
	double mSecondsPerCount;
	double mDeltaTime;

	__int64 mBaseTime;
	__int64 mPausedTime;
	__int64 mStopTime;
	__int64 mPrevTime;
	__int64 mCurrTime;

	bool mStopped;
};

#endif // GAMETIMER_H
#include <Windows.h>
#include <memory>
#include "GameTimer.h"

GameTimer::GameTimer() 
	: mSecondsPerCount(0.0), mDeltaTime(0.0), mBaseTime(0), mStopTime(0),
	mPausedTime(0), mPrevTime(0), mCurrTime(0), mStopped(false)
{
	__int64 countsPerSec;
	QueryPerformanceFrequency((LARGE_INTEGER*)&countsPerSec);
	mSecondsPerCount = 1 / static_cast<double>(countsPerSec);
}

float GameTimer::TotalTime() const
{
	return static_cast<float>((((mStopped ? mStopTime : mCurrTime) - mPausedTime) - mBaseTime) * mSecondsPerCount);
}

float GameTimer::DeltaTime() const
{
	return static_cast<float>(mDeltaTime);
}

void GameTimer::Reset()
{
	__int64 currTime;
	QueryPerformanceCounter((LARGE_INTEGER*)&currTime);

	mBaseTime = currTime;
	mPrevTime = currTime;
	mStopTime = 0;
	mStopped = false;
}

void GameTimer::Start()
{
	__int64 startTime;
	QueryPerformanceCounter((LARGE_INTEGER*)&startTime);
	if (mStopped)
	{
		mPausedTime += (startTime - mStopTime);
		
		mPrevTime = startTime;
		mStopTime = 0;
		mStopped = false;
	}
}

void GameTimer::Stop()
{
	if (!mStopped)
	{
		__int64 currTime;
		QueryPerformanceCounter((LARGE_INTEGER*)(&currTime));

		mStopTime = currTime;
		mStopped = true;
	}
}

void GameTimer::Tick()
{
	if (mStopped)
	{
		mDeltaTime = 0.0;
		return;
	}

	__int64 currTime;
	QueryPerformanceCounter((LARGE_INTEGER*)&currTime);
	mCurrTime = currTime;

	mDeltaTime = (mCurrTime - mPrevTime) * mSecondsPerCount;

	mPrevTime = mCurrTime;

	if (mDeltaTime < 0.0)
	{
		mDeltaTime = 0.0;
	}
}

 

GameTimer는 DirectX 예시에서 시간과 관련된 기능들이 구현되어 있습니다.

게임 개발 할 때 초당 Update를 처리하는 Tick이라던가.

이 Tick이 호출되는 주기라던가. 경과 시간이라던가.

 

GameTimer 또한 지원해주는 함수는 아닙니다.

VS에서 지원하는 DirectX12 Template에서는 Windows에서 사용하는 Time 관련 class를 사용하기 때문이죠.

하지만 그쪽 예시는 기본적으로 GPU 사용과 Multicore를 기본 전제로 깔면서,

Windows App Programming에서 요구하는 최신 문법들을 사용하기 때문에 입문을 하는데에 큰 어려움이 따릅니다.

때문에 가능하면 이 함수를 따로 구현하는 것을 추천합니다.

 

대부분의 함수 기능과 변수 역할은 코드를 읽으면 충분히 이해가 갈 것입니다.

그 중 낯선 것이 있다면 QueryPerformanceCounter, QueryPerformanceFrequency인데, 

이는 고해상도 타이머의 Clock수와 주파수를 받아오는 함수입니다.

대체로 CPU의 것을 주로 사용합니다.

http://www.tipssoft.com/bulletin/board.php?bo_table=FAQ&wr_id=735

 

팁스소프트 > MFC/API 가이드 > [Tip] QueryPerformanceCounter 함수에 대하여...

팁스소프트에서 제공하는 프로그래밍과 관련된 자료나 정보들을 무단으로 복제하거나 게재하는 행위는상호간의 신뢰를 무너뜨리는 행위이며, 법적인 문제를 야기할 수 있으므로 각별한 주의��

www.tipssoft.com

Common

#pragma comment(lib, "d3dcompiler.lib")
#pragma comment(lib, "D3D12.lib")
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "dxcompiler.lib")

#pragma once
#include <Windows.h>
#include <wrl.h>
#include <windowsx.h>
#include <d3d12.h>
#include <dxgi1_6.h>
#include <d3dcompiler.h>
#include <dxcapi.h>
#include <DirectXMath.h>
#include <pix.h>
#include <DirectXPackedVector.h>
#include <DirectXColors.h>
#include <DirectXCollision.h>
#include <string>
#include <vector>
#include <array>
#include <unordered_map>
#include <algorithm>
#include <memory>
#include "GameTimer.h"
#include "DXException.h"
#include "../resource.h"

#include <iostream>

#if defined(DEBUG) || defined(_DEBUG)
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif

#define MAX_LOADSTRING 100

// Global Variables:
//HINSTANCE hInst;                                // current instance
//WCHAR szTitle[MAX_LOADSTRING];                  // The title bar text
//WCHAR szWindowClass[MAX_LOADSTRING];            // the main window class name

inline std::wstring AnsiToWString(const std::string& str)
{
	WCHAR buffer[512];
	MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, buffer, 512);
	return std::wstring(buffer);
}

#ifndef ThrowIfFailed
#define ThrowIfFailed(x)							\
{													\
	HRESULT hr__ = (x);								\
	std::wstring wfn = AnsiToWString(__FILE__);		\
	if(FAILED(hr__))								\
	{												\
		throw DXException(hr__, L#x, wfn, __LINE__);\
	}												\
}
#endif

template<typename T>
static T Clamp(const T& x, const T& low, const T& high)
{
	return x < low ? low : (x > high ? high : x);
}

static UINT calcConstantBufferByteSize(UINT byteSize)
{
	return (byteSize + 255) & ~255;
}

static Microsoft::WRL::ComPtr<ID3DBlob> CompileShader(
	const std::wstring& filename,
	const D3D_SHADER_MACRO* defines,
	const std::string& entrypoint,
	const std::string& target)
{
	UINT compileFlags = 0;

#if defined(DEBUG) || defined(_DEBUG)
	compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
#endif

	HRESULT hr = S_OK;

	Microsoft::WRL::ComPtr<ID3DBlob> byteCode = nullptr;
	Microsoft::WRL::ComPtr<ID3DBlob> errors = nullptr;

	hr = D3DCompileFromFile(filename.c_str(), defines, D3D_COMPILE_STANDARD_FILE_INCLUDE, 
		entrypoint.c_str(), target.c_str(), compileFlags, 0, &byteCode, &errors);

	if (errors != nullptr)
	{
		OutputDebugStringA(static_cast<char*>(errors->GetBufferPointer()));
	}

	ThrowIfFailed(hr);

	return byteCode;
}


//------------------------------------------------------------------------------------------------
// Row-by-row memcpy
inline void MemcpySubresource(
	_In_ const D3D12_MEMCPY_DEST* pDest,
	_In_ const D3D12_SUBRESOURCE_DATA* pSrc,
	SIZE_T RowSizeInBytes,
	UINT NumRows,
	UINT NumSlices)
{
	for (UINT z = 0; z < NumSlices; ++z)
	{
		BYTE* pDestSlice = reinterpret_cast<BYTE*>(pDest->pData) + pDest->SlicePitch * z;
		const BYTE* pSrcSlice = reinterpret_cast<const BYTE*>(pSrc->pData) + pSrc->SlicePitch * z;
		for (UINT y = 0; y < NumRows; ++y)
		{
			memcpy(pDestSlice + pDest->RowPitch * y,
				pSrcSlice + pSrc->RowPitch * y,
				RowSizeInBytes);
		}
	}
}


//------------------------------------------------------------------------------------------------
// All arrays must be populated (e.g. by calling GetCopyableFootprints)
inline UINT64 UpdateSubresources(
	_In_ ID3D12GraphicsCommandList* pCmdList,
	_In_ ID3D12Resource* pDestinationResource,
	_In_ ID3D12Resource* pIntermediate,
	_In_range_(0, D3D12_REQ_SUBRESOURCES) UINT FirstSubresource,
	_In_range_(0, D3D12_REQ_SUBRESOURCES - FirstSubresource) UINT NumSubresources,
	UINT64 RequiredSize,
	_In_reads_(NumSubresources) const D3D12_PLACED_SUBRESOURCE_FOOTPRINT* pLayouts,
	_In_reads_(NumSubresources) const UINT* pNumRows,
	_In_reads_(NumSubresources) const UINT64* pRowSizesInBytes,
	_In_reads_(NumSubresources) const D3D12_SUBRESOURCE_DATA* pSrcData)
{
	// Minor validation
	D3D12_RESOURCE_DESC IntermediateDesc = pIntermediate->GetDesc();
	D3D12_RESOURCE_DESC DestinationDesc = pDestinationResource->GetDesc();
	if (IntermediateDesc.Dimension != D3D12_RESOURCE_DIMENSION_BUFFER ||
		IntermediateDesc.Width < RequiredSize + pLayouts[0].Offset ||
		RequiredSize >(SIZE_T) - 1 ||
		(DestinationDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER &&
			(FirstSubresource != 0 || NumSubresources != 1)))
	{
		return 0;
	}

	BYTE* pData;
	HRESULT hr = pIntermediate->Map(0, NULL, reinterpret_cast<void**>(&pData));
	if (FAILED(hr))
	{
		return 0;
	}

	for (UINT i = 0; i < NumSubresources; ++i)
	{
		if (pRowSizesInBytes[i] > (SIZE_T)-1) return 0;
		D3D12_MEMCPY_DEST DestData = { pData + pLayouts[i].Offset, pLayouts[i].Footprint.RowPitch, pLayouts[i].Footprint.RowPitch * pNumRows[i] };
		MemcpySubresource(&DestData, &pSrcData[i], (SIZE_T)pRowSizesInBytes[i], pNumRows[i], pLayouts[i].Footprint.Depth);
	}
	pIntermediate->Unmap(0, NULL);

	if (DestinationDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)
	{
		D3D12_BOX SrcBox{ static_cast<UINT>(pLayouts[0].Offset), 0, 0, static_cast<UINT>(pLayouts[0].Offset + pLayouts[0].Footprint.Width), 1, 1 };
		//CD3DX12_BOX SrcBox(UINT(pLayouts[0].Offset), UINT(pLayouts[0].Offset + pLayouts[0].Footprint.Width));
		pCmdList->CopyBufferRegion(
			pDestinationResource, 0, pIntermediate, pLayouts[0].Offset, pLayouts[0].Footprint.Width);
	}
	else
	{
		for (UINT i = 0; i < NumSubresources; ++i)
		{
			D3D12_TEXTURE_COPY_LOCATION Dst{ pDestinationResource, D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT, i + FirstSubresource };
			D3D12_TEXTURE_COPY_LOCATION Src{ pIntermediate, D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT, pLayouts[i] };
			//CD3DX12_TEXTURE_COPY_LOCATION Dst(pDestinationResource, i + FirstSubresource);
			//CD3DX12_TEXTURE_COPY_LOCATION Src(pIntermediate, pLayouts[i]);
			pCmdList->CopyTextureRegion(&Dst, 0, 0, 0, &Src, nullptr);
		}
	}
	return RequiredSize;
}

//------------------------------------------------------------------------------------------------
// Heap-allocating UpdateSubresources implementation
inline UINT64 UpdateSubresources(
	_In_ ID3D12GraphicsCommandList* pCmdList,
	_In_ ID3D12Resource* pDestinationResource,
	_In_ ID3D12Resource* pIntermediate,
	UINT64 IntermediateOffset,
	_In_range_(0, D3D12_REQ_SUBRESOURCES) UINT FirstSubresource,
	_In_range_(0, D3D12_REQ_SUBRESOURCES - FirstSubresource) UINT NumSubresources,
	_In_reads_(NumSubresources) D3D12_SUBRESOURCE_DATA* pSrcData)
{
	UINT64 RequiredSize = 0;
	UINT64 MemToAlloc = static_cast<UINT64>(sizeof(D3D12_PLACED_SUBRESOURCE_FOOTPRINT) + sizeof(UINT) + sizeof(UINT64)) * NumSubresources;
	if (MemToAlloc > SIZE_MAX)
	{
		return 0;
	}
	void* pMem = HeapAlloc(GetProcessHeap(), 0, static_cast<SIZE_T>(MemToAlloc));
	if (pMem == NULL)
	{
		return 0;
	}
	D3D12_PLACED_SUBRESOURCE_FOOTPRINT* pLayouts = reinterpret_cast<D3D12_PLACED_SUBRESOURCE_FOOTPRINT*>(pMem);
	UINT64* pRowSizesInBytes = reinterpret_cast<UINT64*>(pLayouts + NumSubresources);
	UINT* pNumRows = reinterpret_cast<UINT*>(pRowSizesInBytes + NumSubresources);

	D3D12_RESOURCE_DESC Desc = pDestinationResource->GetDesc();
	ID3D12Device* pDevice;
	pDestinationResource->GetDevice(__uuidof(*pDevice), reinterpret_cast<void**>(&pDevice));
	pDevice->GetCopyableFootprints(&Desc, FirstSubresource, NumSubresources, IntermediateOffset, pLayouts, pNumRows, pRowSizesInBytes, &RequiredSize);
	pDevice->Release();

	UINT64 Result = UpdateSubresources(pCmdList, pDestinationResource, pIntermediate, FirstSubresource, NumSubresources, RequiredSize, pLayouts, pNumRows, pRowSizesInBytes, pSrcData);
	HeapFree(GetProcessHeap(), 0, pMem);
	return Result;
}

static Microsoft::WRL::ComPtr<ID3D12Resource> CreateDefaultbuffer(
	ID3D12Device* device,
	ID3D12GraphicsCommandList* cmdList,
	const void* initData,
	UINT64 byteSize,
	Microsoft::WRL::ComPtr<ID3D12Resource>& uploadBuffer)
{
	Microsoft::WRL::ComPtr<ID3D12Resource> defaultBuffer;

	D3D12_HEAP_PROPERTIES BeforeDefaultHeapProperty{ D3D12_HEAP_TYPE_DEFAULT, D3D12_CPU_PAGE_PROPERTY_UNKNOWN, D3D12_MEMORY_POOL_UNKNOWN, 1, 1 };
	D3D12_RESOURCE_DESC BeforeDefaultResource{
		D3D12_RESOURCE_DIMENSION_BUFFER, 
		0, byteSize, 1, 1, 1, 
		DXGI_FORMAT_UNKNOWN, 1, 0, 
		D3D12_TEXTURE_LAYOUT_ROW_MAJOR, 
		D3D12_RESOURCE_FLAG_NONE };
	ThrowIfFailed(device->CreateCommittedResource(
		&BeforeDefaultHeapProperty,
		D3D12_HEAP_FLAG_NONE,
		&BeforeDefaultResource,
		D3D12_RESOURCE_STATE_COMMON,
		nullptr,
		IID_PPV_ARGS(defaultBuffer.GetAddressOf())
	));

	D3D12_HEAP_PROPERTIES AfterUploadHeapProperty{ D3D12_HEAP_TYPE_UPLOAD, D3D12_CPU_PAGE_PROPERTY_UNKNOWN, D3D12_MEMORY_POOL_UNKNOWN, 1, 1 };
	D3D12_RESOURCE_DESC AfterUploadResource{
		D3D12_RESOURCE_DIMENSION_BUFFER,
		0, byteSize, 1, 1, 1,
		DXGI_FORMAT_UNKNOWN, 1, 0,
		D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
		D3D12_RESOURCE_FLAG_NONE };
	ThrowIfFailed(device->CreateCommittedResource(
		&AfterUploadHeapProperty,
		D3D12_HEAP_FLAG_NONE,
		&AfterUploadResource,
		D3D12_RESOURCE_STATE_GENERIC_READ,
		nullptr,
		IID_PPV_ARGS(uploadBuffer.GetAddressOf())
	));

	D3D12_SUBRESOURCE_DATA subResourceData = {};
	subResourceData.pData = initData;
	subResourceData.RowPitch = static_cast<LONG_PTR>(byteSize);
	subResourceData.SlicePitch = subResourceData.RowPitch;



	D3D12_RESOURCE_BARRIER BeforeBarrier;
	ZeroMemory(&BeforeBarrier, sizeof(BeforeBarrier));
	BeforeBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
	BeforeBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
	BeforeBarrier.Transition.pResource = defaultBuffer.Get();
	BeforeBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COMMON;
	BeforeBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST;
	BeforeBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
	cmdList->ResourceBarrier(1, &BeforeBarrier);

	UpdateSubresources(cmdList, defaultBuffer.Get(), uploadBuffer.Get(), 0, 0, 1, &subResourceData);

	D3D12_RESOURCE_BARRIER AfterBarrier;
	ZeroMemory(&AfterBarrier, sizeof(AfterBarrier));
	AfterBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
	AfterBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
	AfterBarrier.Transition.pResource = defaultBuffer.Get();
	AfterBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
	AfterBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_GENERIC_READ;
	AfterBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
	cmdList->ResourceBarrier(1, &AfterBarrier);

	return defaultBuffer;
}

Common.h는 크게 3가지 기능을 가지고 있습니다.첫번째는 DirectX Library의 링크. 가장 위의 4개 줄이 이 역할을 맡습니다.두번째는 DirectX 예시 작성에 필요한 Header Include.세번째는 DirectX 예시 전반적으로 요구되는 공통 함수들이 선언되어 있습니다.원래 예시에서는 D3DUtill이었으며, 위에서 설명한 DXException이나 GameTimer도 원래 이 안에 선언되어 있었습니다.하지만 Class 단위의 것들은 별도의 파일로 분리를 하면서 따로 작성이 되었습니다.

 

우선은 이대로 사용을 하고, 어느정도 추가 될 내용이 없어지면 다시 한번 정리를 할 예정입니다.

MeshGeometry

#pragma once
#include "Common.h"

struct SubmeshGeometry
{
	UINT IndexCount = 0;
	UINT StartIndexLocation = 0;
	INT BaseVertexLocation = 0;

	DirectX::BoundingBox Bounds;
};

struct MeshGeometry
{
	std::string Name;

	Microsoft::WRL::ComPtr<ID3DBlob> VertexBufferCPU = nullptr;
	Microsoft::WRL::ComPtr<ID3DBlob> IndexBufferCPU = nullptr;

	Microsoft::WRL::ComPtr<ID3D12Resource> VertexBufferGPU = nullptr;
	Microsoft::WRL::ComPtr<ID3D12Resource> IndexBufferGPU = nullptr;

	Microsoft::WRL::ComPtr<ID3D12Resource> VertexBufferUploader = nullptr;
	Microsoft::WRL::ComPtr<ID3D12Resource> IndexBufferUploader = nullptr;

	UINT VertexByteStride = 0;
	UINT VertexBufferByteSize = 0;
	DXGI_FORMAT IndexFormat = DXGI_FORMAT_R16_UINT;
	UINT IndexBufferByteSize = 0;

	std::unordered_map<std::string, SubmeshGeometry> DrawArgs;

	D3D12_VERTEX_BUFFER_VIEW VertexBufferView() const
	{
		D3D12_VERTEX_BUFFER_VIEW vbv;
		vbv.BufferLocation = VertexBufferGPU->GetGPUVirtualAddress();
		vbv.StrideInBytes = VertexByteStride;
		vbv.SizeInBytes = VertexBufferByteSize;

		return vbv;
	}
	
	D3D12_INDEX_BUFFER_VIEW IndexBufferView() const
	{
		D3D12_INDEX_BUFFER_VIEW ibv;
		ibv.BufferLocation = IndexBufferGPU->GetGPUVirtualAddress();
		ibv.Format = IndexFormat;
		ibv.SizeInBytes = IndexBufferByteSize;

		return ibv;
	}
};

MeshGeometry는 DirectX 예시 코드에서 사용하는 Model 객체입니다.

Resource를 읽고 이를 기반으로 Vertex, Index를 내부에서 관리하면서 BufferView를 제공합니다.

간단하게 "Resource를 이렇게 관리한다."와 "이러이러한 정보들을 받아올 수 있다."를 파악하면 될 것 같습니다.

UploadBuffer

#pragma once
#include "Common.h"

template<typename T>
class UploadBuffer
{
public:
	UploadBuffer(ID3D12Device* device, UINT elementCount, bool isConstantBuffer) : mIsConstantBuffer(isConstantBuffer)
	{
		mElementByteSize = sizeof(T);
		
		if (isConstantBuffer)
		{
			mElementByteSize = calcConstantBufferByteSize(sizeof(T));
		}
		D3D12_HEAP_PROPERTIES HeapProperty;
		HeapProperty.Type = D3D12_HEAP_TYPE_UPLOAD;
		HeapProperty.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
		HeapProperty.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
		HeapProperty.CreationNodeMask = 1;
		HeapProperty.VisibleNodeMask = 1;

		D3D12_RESOURCE_DESC Resource;
		Resource.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
		Resource.Alignment = 0;
		Resource.Width = mElementByteSize * static_cast<UINT64>(elementCount);
		Resource.Height = 1;
		Resource.DepthOrArraySize = 1;
		Resource.MipLevels = 1;
		Resource.Format = DXGI_FORMAT_UNKNOWN;
		Resource.SampleDesc.Count = 1;
		Resource.SampleDesc.Quality = 0;
		Resource.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
		Resource.Flags = D3D12_RESOURCE_FLAG_NONE;

		ThrowIfFailed(device->CreateCommittedResource(
			&HeapProperty,
			D3D12_HEAP_FLAG_NONE,
			&Resource,
			D3D12_RESOURCE_STATE_GENERIC_READ,
			nullptr,
			IID_PPV_ARGS(&mUploadBuffer)
		));

		ThrowIfFailed(mUploadBuffer->Map(0, nullptr, reinterpret_cast<void**>(&mMappedData)));
	}
	UploadBuffer(const UploadBuffer& rhs) = delete;
	UploadBuffer& operator=(const UploadBuffer& rhs) = delete;
	~UploadBuffer()
	{
		if (mUploadBuffer != nullptr)
		{
			mUploadBuffer->Unmap(0, nullptr);
		}
		mMappedData = nullptr;
	}

	ID3D12Resource* Resource() const
	{
		return mUploadBuffer.Get();
	}

	void CopyData(int elementIndex, const T& data)
	{
		memcpy(&mMappedData[elementIndex * mElementByteSize], &data, sizeof(T));
	}

private:
	Microsoft::WRL::ComPtr<ID3D12Resource> mUploadBuffer;
	BYTE* mMappedData = nullptr;

	UINT mElementByteSize = 0;
	bool mIsConstantBuffer = false;
};

UploadBuffer는 DirectX에서 사용하는 Buffer의 기본적인 기능을 구현한 것입니다.

복사생성자와 대입연산자 생성을 막아서 복사를 하지 못하게 하였고,

별도의 데이터 복사나 버퍼의 포인터를 따로 제공하는 함수가 있는 Proxy Class입니다.

 

사실 여기서부터 기존 코드와 차이가 발생하는데,

Windows에서 제공하는 구현체를 사용하지 않은 탓에 굉장히 긴 초기화 과정을 거치는 경우가 있습니다.

처음에는 배울 때에는 외부 코드를 사용하지 않는 것이 좋다고 생각 했습니다.

하지만 한번 해보니, 아무리 생각해도 이러한 코드들 없이 개발 하는 것은 좀 말이 안된다는 생각이 들었습니다.

그래서 이 부분은 코드 설명이 다 끝난 뒤, 대대적으로 수정을 할 예정입니다.

 

다음에는 DirectX 기본 코드인 D3DApp을 살펴보도록 하겠습니다.

'내용정리 > DirectX12' 카테고리의 다른 글

10. Box Example 3 - BoxApp, WinMain  (0) 2020.09.15
10. Box Example 2 - D3DApp  (0) 2020.09.08
20.08.25 개발일지  (0) 2020.08.25
20.08.21 개발일지  (0) 2020.08.21
20.08.18 개발일지  (0) 2020.08.18

면접을 보고 왔더니 체력도 정신력도 오링났습니다.

살 빠지면서 체력이 좋아진줄 알았는데 회사 근무하게 된다면 아침 점심으로 커피 꼭 챙겨 마셔야겠다고 느꼈습니다.

 

좀만 하면 되는데 그게 안되네요.

낮잠 좀 자면서 쉬겠습니다...

'개발일지' 카테고리의 다른 글

20.09.11 비개발일지  (0) 2020.09.11
20.09.10 비개발일지  (0) 2020.09.10
20.09.01 비개발일지  (0) 2020.09.01
20.08.29 비개발일지  (0) 2020.08.29
20.08.28 비개발일지  (0) 2020.08.28

다시 한번 Character의 생성자에서 UpdateCharacterState로 초기화를 해보았으나

정상적인 동작을 이끌어낼 수 없었습니다.

 

내일은 면접이라 특별히 무언가 할 수 없고,

다음에 시간이 된다면 이번에는 Character쪽에 Replicate 함수를 써볼까 합니다.

일반 Actor는 기본적인 Replicate 설정이 없지만, Character는 기본 설정이 되어 있지 않을까 의심이 되기 때문입니다.

 

이렇게 해서 문제가 풀리면 그대로 승천 할것만 같습니다.

'개발일지 > Treasure Hunter' 카테고리의 다른 글

20.09.07 개발일지  (0) 2020.09.07
20.09.05 개발일지  (0) 2020.09.05
20.08.21 개발일지  (0) 2020.08.31
20.08.27 개발일지  (0) 2020.08.27
20.08.24 개발일지  (0) 2020.08.24

면접을 보느라 코드를 들여다보지 못했습니다.

남은 고정 일정은 이번주 목요일, 다음주 목요일입니다.

이번주에 하루 열심히 봐서 가능하면 더 미루지 않으려 합니다.

'개발일지' 카테고리의 다른 글

20.09.10 비개발일지  (0) 2020.09.10
20.09.03 비개발일지  (0) 2020.09.03
20.08.29 비개발일지  (0) 2020.08.29
20.08.28 비개발일지  (0) 2020.08.28
20.08.26 비개발일지  (0) 2020.08.26

+ Recent posts