공부중

[DirectX12]Hello Wolrd Sample - D3D12HelloWindow - 4 본문

Programing/DirectX

[DirectX12]Hello Wolrd Sample - D3D12HelloWindow - 4

곤란 2018. 4. 8. 23:15
반응형
        ComPtr<IDXGIFactory4> factory;
	ThrowIfFailed(CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&factory)));

	if (m_useWarpDevice)
	{
		ComPtr<IDXGIAdapter> warpAdapter;
		ThrowIfFailed(factory->EnumWarpAdapter(IID_PPV_ARGS(&warpAdapter)));

		ThrowIfFailed(D3D12CreateDevice(
			warpAdapter.Get(),
			D3D_FEATURE_LEVEL_11_0,
			IID_PPV_ARGS(&m_device)
			));
	}
	else
	{
		ComPtr<IDXGIAdapter1> hardwareAdapter;
		GetHardwareAdapter(factory.Get(), &hardwareAdapter);

		ThrowIfFailed(D3D12CreateDevice(
			hardwareAdapter.Get(),
			D3D_FEATURE_LEVEL_11_0,
			IID_PPV_ARGS(&m_device)
			));
	}

ComPtr로 IDXGIFactorty4를 만들었는데 앞에 대문자I가 붙어있으면 Interface라고 생각하면 된다.

IDXGIFactorty4는 IDXGISwapChain인터페이스 생성과 어댑터 관련에 쓰인다.

이 어댑터를 가지고 디바이스를 만든다. (D3D12CreateDevice 함수를 통하여 생성시 필요)

CreateDXGIFactory2를 가지고 팩토리 인터페이스를 만들어 준다. 전달받는 인자는 위에서 생성한 factory 변수와 Flag

 

여기서 플래그(Flag)는 위의 코드에서 UINT형으로 만든 dxgiFactoryFlags를 넘겨주었다.

유요한 플래그는 2개로 다음과 같다.

 DXGI_CREATE_FACTORY_DEBUG (0x01) 과 0. - 출처 : MSDN 링크

 

 

다음 코드는 DXSample의 생성자에서 m_useWarpDevice의 값을 false로 초기화 헀는데 만일 true로 했다면 if문 안의 내용이 실행될 것이고

변경하지 않았다면(false) else문의 문구가 실행 될것이다.

 

일단 디바이스는 디스플레이 어댑터를 나타내는 객체라고 생각하고 

물리적인 그래픽 하드웨어 장치( 그래픽카드 - Geforce 그래픽카드 , Radeon(AMD) 그래픽카드 , Intel 내장그래픽 기타 등등등... )

하드웨어 그래픽을 흉내내는 소프트웨어 디스플레이 어댑터(WARP 어댑터)

로 나뉜다고 보면 된다.

 

당연히 소프트웨어 디스플레이 어댑터가 더 느리다 -_-... ( 되도록 지원하는 하드웨어를 장착해서 개발을 하는게 스트레스 안쌓인다 -_-; )

 

이어서 m_useWarpDevice가 참(true)일 경우일때를 보자

ComPtr로 IDXGIAdapter 형의 어댑터 저장 공간을 만들어주고 

위에서 생성한 factory 인터페이스에서 EnumWarpAdapter를 가지고만들어준다.

EnumWarpAdapter(MSDN링크)의 리턴값은 성공시 S_OK 실패시 에러코드를 뿜는다. ( 에러 목록은 DXGI_ERROR 을 살펴봐야한다 - 링크 )

( 혹시 Direct3D 12 Return Codes 코드도 MSDN에서 살펴보라고 했으니 살펴봐야한다. - 링크 )

ThrowIfFailed는 그냥 이름대로 해석해서 이해하면 된다. 만일 실패시 던진다

 

//
// FileName : DXSampleHelper.h
//
inline void ThrowIfFailed(HRESULT hr)
{
	if (FAILED(hr))
	{
		throw std::exception();
	}
}

DXSampleHelper.h 는 공식 dx함수가 아닌 샘플에서 만들어놓은 헤더이므로 참고.

 

이제 D3D12CreateDevice 함수를 이용해서 디바이스를 생성한다.

 

HRESULT WINAPI D3D12CreateDevice(
    _In_opt_ IUnknown* pAdapter,
    D3D_FEATURE_LEVEL MinimumFeatureLevel,
    _In_ REFIID riid, // Expected: ID3D12Device
    _COM_Outptr_opt_ void** ppDevice );
  • pAdapter는 디바이스가 나타내는 어댑터를 넘겨준다. 매개변수에 nullptr을 넣으면 시스템의 기본 디스플레이 어댑터가 쓰인다.
  • MinimumFeatureLevel은 요구하는 최소 기능 수준이고 어댑터가 최소 이 수준을 지원안하면 디바이스 생성에 실패한다.

코드에 적힌 D3D_FEATURE_LEVEL_11_0은 Direct3D 11.0이라고 보면 된다.

D3D_FEATURE_LEVEL은 아래와 같이 선언되어 있다.

 

typedef 
enum D3D_FEATURE_LEVEL
    {
        D3D_FEATURE_LEVEL_9_1	= 0x9100,
        D3D_FEATURE_LEVEL_9_2	= 0x9200,
        D3D_FEATURE_LEVEL_9_3	= 0x9300,
        D3D_FEATURE_LEVEL_10_0	= 0xa000,
        D3D_FEATURE_LEVEL_10_1	= 0xa100,
        D3D_FEATURE_LEVEL_11_0	= 0xb000,
        D3D_FEATURE_LEVEL_11_1	= 0xb100,
        D3D_FEATURE_LEVEL_12_0	= 0xc000,
        D3D_FEATURE_LEVEL_12_1	= 0xc100
    } 	D3D_FEATURE_LEVEL;

Windows SDK 16299 버전에는 12_1 까지 나와있다. 추후에 더 추가가 될 수도 있다.

 

  • riid 는 생성하조가 하는 ID3D12Device  인터페이스의 COM ID
  • ppDevice는 생성된 장치가 이 매개변수에 설정된다 ( 출력 매개변수 )

riid와 ppDevice는 IID_PPV_ARGS 매크로를 통해서 같이 넘겨준다.

 

D3D12CreateDevice의 리턴값은 이 메서드는 Direct3D 12 반환 코드 중 하나를 반환 할 수 있습니다. 가능한 반환 값에는 CreateDXGIFactory1 및 IDXGIFactory :: EnumAdapters에 대해 설명 된 값이 포함됩니다. 라고 MSDN에 적혀 있다. 

Direct3D 12 Return Codes - MSDN 링크

 

하단의 m_useWarpDevice가 false인 경우도 true인 경우와 별 다른점이 없다.

다른점이라면 GetHardwareAdapter에서 하드웨어 어댑터를 받아와 D3D12CreateDevice에 넘겨주는 작업이 다를뿐이다.

 

근데 GetHardwareAdapter는 DXSample의 멤버함수로써 기본 direct 라이브러리에 포함된것이 아니다 -_-...

GetHardwareAdapter를 보자

// GetHardwareAdapter(factory.Get(), &hardwareAdapter); 로 호출

// Direct3D 12를 지원하는 첫 번째 사용 가능한 하드웨어 어댑터를 얻기위한 도우미 함수.
// 그러한 어댑터를 찾을 수없는 경우 * ppAdapter는 nullptr로 설정됩니다.
_Use_decl_annotations_
void DXSample::GetHardwareAdapter(IDXGIFactory2* pFactory, IDXGIAdapter1** ppAdapter)
{
	ComPtr<IDXGIAdapter1> adapter;
	*ppAdapter = nullptr;

	for (UINT adapterIndex = 0; DXGI_ERROR_NOT_FOUND != pFactory->EnumAdapters1(adapterIndex, &adapter); ++adapterIndex)
	{
		DXGI_ADAPTER_DESC1 desc;
		adapter->GetDesc1(&desc);

		if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)
		{
			// 기본 렌더링 드라이버 어댑터를 선택하지 마십시오.
                        // 소프트웨어 어댑터가 필요한 경우 명령 줄에서 "/ warp"를 전달하십시오.
			continue;
		}

		// 어댑터가 Direct3D 12를 지원하는지 확인하지만
                // 실제 장치는 아직 없습니다.
		if (SUCCEEDED(D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr)))
		{
			break;
		}
	}

	*ppAdapter = adapter.Detach();
}

ComPtr로 IDXGIAdapter1 형의 adapter 변수를 만들고

전달 받은 ppAdapter는 초기화를 안해준채로 넘겨주어서 그런지 nullptr로 값을 넣었다.

반복문을 통해서 설치된 어댑터들(그래픽카드들)을 확인한다.

 

factory의 멤버함수로 EnumAdapters1를 통해 어댑터의 인덱스를 전달받아 인덱스에 해당하는 어댑터를 받아온다.

받아온 어댑터의 정보로 소프트웨어 어댑터이면 continue 아니면 D3D12CreateDevice를 통해서 디바이스 생성에 성공하는지 확인해본다.

마지막으로 adapter.Detach(); 가 있는데 Detach( )는 ComPtr의 함수이며 ComPtr객체가 나타내는 인터페이스에 대한 포인터를 리턴하는 함수다

ComPtr::Detach Method - MSDN 링크 )

 

GetHardwareAdapter 함수 실행을 마치고 그 뒤에 실행하는 D3D12CreateDevice는 위의 warpAdapter용으로 생성하는것과 다른것이 없으므로

패스!

 

다음글로!

 

반응형