공부중

[DirectX12]Hello Wolrd Sample - D3D12HelloTriangle - 5 본문

Programing/DirectX

[DirectX12]Hello Wolrd Sample - D3D12HelloTriangle - 5

곤란 2018. 5. 25. 17:48
반응형
	// Create synchronization objects and wait until assets have been uploaded to the GPU.
	{
		ThrowIfFailed(m_device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence)));
		m_fenceValue = 1;

		// Create an event handle to use for frame synchronization.
		m_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
		if (m_fenceEvent == nullptr)
		{
			ThrowIfFailed(HRESULT_FROM_WIN32(GetLastError()));
		}

		// Wait for the command list to execute; we are reusing the same command 
		// list in our main loop but for now, we just want to wait for setup to 
		// complete before continuing.
		WaitForPreviousFrame();
	}

이번에 정리해볼 코드이다.

 

동기화 객체를 생성하고 에셋이 GPU에 업로드 될때까지 기다리는 코드이다.

 

하지만 .. 

Fence(울타리)가 어디에 어떻게 쓰이는지 용도를 다시 말하자면

한 시스템에서 두개의 처리장치(CPU와 GPU)가 병렬로 실행되다 보니 여러 동기화 문제가 발생하게 되었고

이를 해결하기 위해서 Fence(울타리)라는 객체를 사용하는것이다.

일단 코드를 차근차근 보자.

 

 

		ThrowIfFailed(m_device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence)));
		m_fenceValue = 1;

 

device에서 CreateFence를 통해 Fence를 생성해 주고 있다.

그리고 fenceValue라는것을 1로 설정해 주었는데 이 Value는 다음에 설명할 예정이다.

일단 Value라는것이 필요하구나 정도로 넘어가자..

 

 

// 프레임 동기화에 사용할 이벤트 핸들을 만듭니다. m_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); if (m_fenceEvent == nullptr) { ThrowIfFailed(HRESULT_FROM_WIN32(GetLastError())); }

Event를 생성하였는데 이것은 프레임 동기화에 사용할 이벤트 핸들이다.

바로 아래의 if문은 만일 생성 실패시에 처리할 코드이다.

 

 

		// 명령리스트가 실행될 때까지 기다리십시오.  
		// 우리는 메인 루프에서 같은 명령리스트를 재사용하고 있지만, 
		// 지금은 계속하기 전에 설치가 완료되기를 기다리고 싶습니다.
		WaitForPreviousFrame();

 

번역은 번역기를 돌렸기 때문에 번역퀄은... -_-;; 신경쓰지 마세요...

아무튼 저 WaitForPreviousFrame함수 안을 살펴 봐야 할것 같다.

 

 

 

void D3D12HelloTriangle::WaitForPreviousFrame()
{
	// WAITING FOR THE FRAME TO COMPLETE BEFORE CONTINUING IS NOT BEST PRACTICE.
	// This is code implemented as such for simplicity. The D3D12HelloFrameBuffering
	// sample illustrates how to use fences for efficient resource usage and to
	// maximize GPU utilization.

	// Signal and increment the fence value.
	const UINT64 fence = m_fenceValue;
	ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), fence));
	m_fenceValue++;

	// Wait until the previous frame is finished.
	if (m_fence->GetCompletedValue() < fence)
	{
		ThrowIfFailed(m_fence->SetEventOnCompletion(fence, m_fenceEvent));
		WaitForSingleObject(m_fenceEvent, INFINITE);
	}

	m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
}

 

WaitForPreviousFrame 함수의 내부 이다.

이전 프로젝트와 다른 코드의 내용은 없다.

 

그런데 망할 과거의 내가 다음 기회에 알아보자고 해서 지금 적어야 겠다 -_-;;(멍청한 과거의 나...)

 

	// Signal and increment the fence value.
	const UINT64 fence = m_fenceValue;
	ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), fence));
	m_fenceValue++;

LoadAssets함수에서 m_fenceValue 라는 멤버 변수의 값을 1로 설정을 해두었다.

이 멤버변수의 값을 가져와서 Signal이라는 함수를 호출하는데 

MSDN에 적힌 설명을 보면 Updates a fence to a specified value.(Fence를 지정된 값으로 업데이트 한다.) 라고 적혀있다 -_-;;

ID3D12CommandQueue::Signal MSDN 링크

 

ID3D12CommandQueue::Signal은 GPU측에서 Fence값을 설정하고

ID3D12Fence::Signal은 CPU측에서 Fence값을 설정한다고 한다.

 

여기서는 ID3D12CommandQueue::Signal이므로 GPU측에서 Fence값을 설정한다.

GPU가 Signal의 명령까지 모든 명령을 처리하기 전까지 새 Fence(울타리)지점은 설정되지 않는다. 라고 그런 결과가 있기는 한데

지금 프로젝트에서는 삼각형 하나 딸랑 띄우는거라 부하걸리는것도 없고해서 -_-;;;

이게 맞는말인지 확인하기가 거시기하다 ... (누군가 알고있으면 알려주세요 ㅠㅠㅠ)

Signal을 호출한 뒤에 멤버변수인 m_fenceValue를 증가 시키고 있다.

 

 

	// Wait until the previous frame is finished.
	if (m_fence->GetCompletedValue() < fence)
	{
		ThrowIfFailed(m_fence->SetEventOnCompletion(fence, m_fenceEvent));
		WaitForSingleObject(m_fenceEvent, INFINITE);
	}

 

ID3D12Fence::GetCompletedValue 함수를 통해서 Fence의 현재 값을 가져오고 위에서 m_fenceValue에서 가져와 상수로 받은 fence 지역변수와 비교를 하고 있다.

if문의 코드는 주석에 적힌대로 이전 프레임이 끝날때까지 기다리는것이다.

 

이전 프레임이 끝났다면 ID3D12Fence::SetEventOnCompletion를 통해서 울타리가 특정 값에 도달 할 때 발생해야하는 이벤트를 지정해준다.

지정하는 이벤트는 LoadAsset에서 CreateEvent로 만든 m_fenceEvent를 넘겨주고 있다.

 

다음라인에서는 WaitForSingleObject를 통해서 대기해주고 있다.

WaitForSingleObject는 지정된 객체가 신호를 받거나 제한 시간이 경과 될때까지 대기하는 함수이다.

넘겨주는 시간이 INFINITE(#define INFINITE 0xFFFFFFFF  // Infinite timeout)이면 신호를 받을때까지 대기이다.

 

 

	m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();

 

마지막으로 swapChain에서 현재의 BackBuffer의 index를 m_frameIndex 멤버변수에 저장해주고 있다.

 

이로써 LoadAsset 함수가 모두 끝났고

OnInit 도 끝이 났다.

 

이전 글에서도 봤다싶이.

Win32Application::Run에서는 변한 코드가 없고 내부의 pSample의 OnInit에 대해서 변한점이 있어서 알아보았당

다음글은 OnUpdate와 OnRender를 알아보고

 

그리고 혹시나 OnDestory에대해서 알아보면 끝날것 같다.

 

다음글에서....

 

 

 

반응형