[DirectX12] ComPtr Interface

박주형·2022년 11월 18일
0

DirectX12

목록 보기
1/1

개요

ComPtr 클래스

Windows with C++ - COM Smart Pointers Revisited

  • COM 객체는 new로 만들 수 없고 마소의 api로 생성해야 합니다.
  • ComPtr은 COM interface의 전용 공유 포인터로 C++ 표준의 공유 포인터와는 인터페이스 좀 다릅니다.
  • 하지만 사용방법은 C++ 표준과 대동소이합니다.

인터페이스를 설명하기에 앞서…

HRESULT

  • 마소가 사용하는 함수 결과 코드로 대개 마소의 함수들이 성공 여부를 반환하는 타입입니다.

  • Common NULL Facility Error Codes

    NameDescriptionValue
    S_OKOperation successful0x00000000
    S_FALSEOperation successful but returned no results0x00000001
    E_ABORTOperation aborted0x80004004
    E_FAILUnspecified failure0x80004005
    E_NOINTERFACENo such interface supported0x80004002
    E_NOTIMPLNot implemented0x80004001
    E_POINTERPointer that is not valid0x80004003
    E_UNEXPECTEDUnexpected failure0x8000FFFF
  • Common Win32 Facility Error Codes

    NameDescriptionValue
    E_ACCESSDENIEDGeneral access denied error0x80070005
    E_HANDLEHandle that is not valid0x80070006
    E_INVALIDARGOne or more arguments are not valid0x80070057
    E_OUTOFMEMORYFailed to allocate necessary memory0x8007000E

ComPtr의 강참조 횟수를 구하는 헬퍼 함수: ULONG GetRefCount()

template <typename T>
ULONG GetRefCount(const ComPtr<T>& p)
{
	T* temp = p.Get();

	ULONG ret = 0;
	if (temp != nullptr)
	{
		ret = temp->AddRef();
		ret = temp->Release();
	}
	
	return ret;
}
  • ComPtr은 강참조 횟수를 알 수 없기 때문에 헬퍼 함수가 필요합니다.
  • ULONG GetRefCount()는 직접 만든 헬퍼 함수입니다.
  • 밑의 인터페이스를 설명할 때 이 헬퍼 함수를 사용할 것입니다.

IUnknown::QueryInterface

HRESULT hr;
ULONG refCount;

ComPtr<IDXGIFactory4> factory;
hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory));            // S_OK
refCount = GetRefCount(factory);                            // refCount = 1
assert(refCount == 1);

ComPtr<IDXGIFactory6> factory6;
hr = factory->QueryInterface(IID_PPV_ARGS(&factory6));      // S_OK
refCount = GetRefCount(factory);                            // refCount = 2
assert(refCount == 2);

ComPtr<IUnknown> unknown;
hr = factory->QueryInterface(__uuidof(IDXGIFactory6), &unknown);
assert(hr == S_OK);
refCount = GetRefCount(factory);                             // refCount = 3
assert(refCount == 3);

ComPtr 인터페이스

  • 예제들을 테스트하기 위해서는 다음의 코드를 상단에 넣으셔야 합니다.
#define WIN32_LEAN_AND_MEAN  

#include <utility>
#include <cassert>
#include <windows.h>
#include <wrl.h>
#include <dxgi1_6.h>

#pragma comment(lib, "dxgi.lib")

using namespace Microsoft::WRL;

ComPtr::As

// Signature
template<typename U>
HRESULT As(
   _Out_ ComPtr<U>* p
) const;

template<typename U>
HRESULT As(
   _Out_ Details::ComPtrRef<ComPtr<U>> p
) const;
// 예제
HRESULT hr;
ULONG refCount;

ComPtr<IDXGIFactory4> factory;
hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory));            // S_OK
refCount = GetRefCount(factory);                            // refCount = 1
assert(refCount == 1);

ComPtr<IDXGIFactory6> factory6;
hr = factory.As(&factory6);                                 // S_OK
refCount = GetRefCount(factory);                            // refCount = 2
assert(refCount == 2);
  • QueryInterface와 동일한 기능입니다.
  • 타자를 좀 더 적게 칠 수 있다는 강력한 장점이 있습니다.

ComPtr::AsIID

// Signature
WRL_NOTHROW HRESULT AsIID(
   REFIID riid,
   _Out_ ComPtr<IUnknown>* p
) const;
HRESULT hr;
ULONG refCount;

ComPtr<IDXGIFactory4> factory;
hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory));        // S_OK
refCount = GetRefCount(factory);                        // refCount = 1
assert(refCount == 1);

ComPtr<IUnknown> unknown;
hr = factory.AsIID(__uuidof(IDXGIFactory4), &unknown);  // S_OK
refCount = GetRefCount(factory);                        // refCount = 2
assert(refCount == 2);
  • ComPtr::As와 같은 기능인데 ComPtr\에 넣는 것만 다릅니다.
  • __uuidof()는 정확한 기능은 잘 모르겠지만 타입이름이나 변수 이름을 넣어서 guid를 가져온다고 합니다.
    • guid에 대해 궁금하시면 검색해보시길 바랍니다.
    • 어쨋든 unknown에 넣으려면 타입을 가리키는 뭔가가 있어야 하기 때문에 __uuidof()가 당연히 필요하지 않을까 싶습니다.

ComPtr::AsWeak

// Signature
HRESULT AsWeak(
   _Out_ WeakRef* pWeakRef
);

// 예제
WeakRef wr;
strongComptrRef.AsWeak(&wr);

// Now suppose that the object strongComPtrRef points to no longer exists
// and the following code tries to get a strong ref from the weak ref:
ComPtr<ISomeInterface> strongRef;
HRESULT hr = wr.As(&strongRef);

// This check won't work with the Windows 10 SDK version of the library.
// Check the input pointer instead.
if(wr == nullptr)
{
    wprintf(L"Couldn't get strong ref!");
}
  • 약참조 할 수 있는 객체 반환.

  • 약참조를 하려면 약참조하려는 클래스가 IInspectable을 상속해야 하는데 D3D 프로그래밍을 하는데 사용하는 클래스들은 IInspectable을 상속하지 않아서 쓸 수 없습니다.

    • IDXGIObject나 ID3D12Object같이 그래픽 api를 사용하는 인터페이스들은 IInpectable을 상속하지 않아서 약참조를 할 수가 없다는 말입니다.
  • 어차피 다 마소가 만들어준 클래스를 사용하기 때문에 약참조를 사용할 일이 없다고 봅니다.

ComPtr::Attach

// Signature
void Attach(
   _In_opt_ InterfaceType* other
);
HRESULT hr;
ULONG refCount;

ComPtr<IDXGIFactory4> factory;
hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory));  // S_OK
refCount = GetRefCount(factory);                  // 1
assert(refCount == 1);

ComPtr<IDXGIFactory4> factoryAttached;
factoryAttached.Attach(factory.Get());
refCount = GetRefCount(factory);                  // 1
assert(refCount == 1);

// Compile Error! Release() 두번
  • 강참조 횟수를 올리지 않고 참조합니다.
  • 용도가 뭔지 잘 모르겠고, 잘못 사용하면 터지니 주의 요망!
    • 두번 Release()할 수 있기 때문입니다.

ComPtr::ComPtr

// Signature
// #1 기본 생성자
WRL_NOTHROW ComPtr();

// #2 nullptr을 넣는 생성자
WRL_NOTHROW ComPtr(
   decltype(__nullptr)
);

// #3 Raw 포인터를 넣는 생성자
template<class U>
WRL_NOTHROW ComPtr(
   _In_opt_ U *other
);

// #4 복사 생성자
WRL_NOTHROW ComPtr(
   const ComPtr& other
);

// #5 복사 생성자
// U는 other의 타입, T는 현재 타입.
// 즉, U를 T로 변환할 수 있으면 사용할 수 있음.
template<class U>
WRL_NOTHROW ComPtr(
   const ComPtr<U> &other,
   typename ENABLE_IF<__is_convertible_to(U*, T*), void *>
);

// #6 이동 생성자
WRL_NOTHROW ComPtr(
   _Inout_ ComPtr &&other
);

// #7 이동 생성자
// #5와 방식이 같음
template<class U>
WRL_NOTHROW ComPtr(
   _Inout_ ComPtr<U>&& other, typename ENABLE_IF<__is_convertible_to(U*, T*), void *>
);
HRESULT hr;
ULONG refCount;

ComPtr<IDXGIFactory4> factory;
hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory));            // S_OK
refCount = GetRefCount(factory);                            // refCount = 1
													        
ComPtr<IDXGIFactory4> factory1();                           // #1
													        
ComPtr<IDXGIFactory4> factory2(nullptr);                    // #2
refCount = GetRefCount(factory);                  	        // refCount = 1        
assert(refCount == 1);

ComPtr<IDXGIFactory4> factory3(factory.Get());              // #3      
refCount = GetRefCount(factory);                            // refCount = 2
assert(refCount == 2);

ComPtr<IDXGIFactory4> factory4(factory);                    // #4
refCount = GetRefCount(factory);                            // refCount = 3
assert(refCount == 3);								        
													        
// ComPtr<IDXGIAdapter> adapter(factory)                    Compile Error!
ComPtr<IDXGIFactory> factory5(factory);                     // #5
refCount = GetRefCount(factory);                            // refCount = 4
assert(refCount == 4);								        
														        
ComPtr<IDXGIFactory4> factory6(std::move(factory));         // #6
assert(factory.Get() == nullptr);					        
refCount = GetRefCount(factory3);                           // refCount = 4
assert(refCount == 4);								        
														        
ComPtr<IDXGIFactory3> factory7(std::move(factory3));        // #7
assert(factory3.Get() == nullptr);
refCount = GetRefCount(factory4);                           // refCount = 4
assert(refCount == 4);
  • ComPtr의 생성자입니다.

ComPtr::CopyTo

// Signature
// #1 
HRESULT CopyTo(
   _Deref_out_ InterfaceType** ptr
);

// #2
// REFIID = const IID&
HRESULT CopyTo(
   REFIID riid,
   _Deref_out_ void** ptr
) const;

// #3
template<typename U>
HRESULT CopyTo(
   _Deref_out_ U** ptr
) const;
HRESULT hr;
ULONG refCount;

ComPtr<IDXGIFactory4> factory;
hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory));            // S_OK
refCount = GetRefCount(factory);                            // refCount = 1
assert(refCount == 1);

ComPtr<IDXGIFactory4> factory1;
factory.CopyTo(&factory1);                                  // #1
refCount = GetRefCount(factory);                            // refCount = 2
assert(refCount == 2);
  • #1은 말 그대로 복사입니다.
  • #2, #3은 사실상 QueryInterface입니다.

ComPtr::Detach

// Signature
T* Detach();
HRESULT hr;
ULONG refCount;

ComPtr<IDXGIFactory4> factory;
hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory));      // S_OK
refCount = GetRefCount(factory);                      // refCount = 1
assert(refCount == 1);
		
ComPtr<IDXGIFactory4> factory1(factory.Detach());     // factory.Get() == nullptr이기 때문에
                                                      // factory의 refCount를 알 수 있는 방법이 없음.

refCount = GetRefCount(factory1);                     // refCount = 2
assert(refCount == 2);
  • 가지고 있는 포인터를 돌려주고 자신의 포인터를 nullptr로 만듭니다.
  • 그런데 내부적으로 Release()를 하지 않기 때문에 강참조 횟수는 하기 전이나 후나 같습니다.
  • 그렇기 때문에 Attach와 함께 결코 사용해선 안될 함수라고 생각합니다.
    • 누수를 유발할 수 있기 때문입니다.

ComPtr::operator&

  • 내부적으로 Release()를 하고 Raw의 주소를 반환합니다.

ComPtr::GetAddressOf, ComPtr::ReleaseAndGetAddressOf

  • Raw의 주소를 반환.
  • ReleaseAndGetAddressOf()는 먼저 Release()를 한 후에 Raw의 주소를 반환한다.

ComPtr::Swap

// Signature
void Swap(
   _Inout_ ComPtr&& r
);

void Swap(
   _Inout_ ComPtr& r
);
  • Raw를 스왑합니다.

ComPtr::operator=

  • 여러 오버로드가 있고 모두 확인해 봤는데 원래 가지고 있던 Raw의 강참조를 줄이고 복사시키거나 이동시킵니다.
  • 즉, 원래 가지고 있던 Raw→Release()는 반드시 호출되니 안심하고 사용해도 되겠습니다.

결론

  • Attach와 Detach는 정말 확신이 없는 이상에는 사용하지 말아야 한다고 생각합니다.
  • 물론 Attach와 Detach는 분명 용도가 있을 것입니다만 저는 별로 자신이 없네요.

전체 테스트 소스코드

#define WIN32_LEAN_AND_MEAN  

#include <utility>
#include <cassert>
#include <windows.h>
#include <wrl.h>
#include <dxgi1_6.h>

#pragma comment(lib, "dxgi.lib")

using namespace Microsoft::WRL;

template <typename T>
ULONG GetRefCount(const ComPtr<T>& p)
{
	T* temp = p.Get();

	ULONG ret = 0;
	if (temp != nullptr)
	{
		ret = temp->AddRef();
		ret = temp->Release();
	}
	
	return ret;
}

int main()
{
	// ************************** QueryInterface **************************
	{
		HRESULT hr;
		ULONG refCount;
	
		ComPtr<IDXGIFactory4> factory;
		hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory));            // S_OK
		refCount = GetRefCount(factory);                            // refCount = 1
		assert(refCount == 1);
	
		ComPtr<IDXGIFactory6> factory6;
		hr = factory->QueryInterface(IID_PPV_ARGS(&factory6));      // S_OK
		refCount = GetRefCount(factory);                            // refCount = 2
		assert(refCount == 2);
	
		ComPtr<IUnknown> unknown;
		hr = factory->QueryInterface(__uuidof(IDXGIFactory6), &unknown);
		assert(hr == S_OK);
		refCount = GetRefCount(factory);                            // refCount = 3
		assert(refCount == 3);
	}
	
	// ************************** As **************************
	{
		HRESULT hr;
		ULONG refCount;
	
		ComPtr<IDXGIFactory4> factory;
		hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory));   // S_OK
		refCount = GetRefCount(factory);                            // refCount = 1
		assert(refCount == 1);
	
		ComPtr<IDXGIFactory6> factory6;
		hr = factory.As(&factory6);                                 // S_OK
		refCount = GetRefCount(factory);                            // refCount = 2
		assert(refCount == 2);
	}
	
	// ************************** AsIID **************************
	{
		HRESULT hr;
		ULONG refCount;
	
		ComPtr<IDXGIFactory4> factory;
		hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory));             // S_OK
		refCount = GetRefCount(factory);                             // refCount = 1
		assert(refCount == 1);
	
		ComPtr<IUnknown> unknown;
		hr = factory.AsIID(__uuidof(IDXGIFactory4), &unknown);       // S_OK
		refCount = GetRefCount(factory);                             // refCount = 2
		assert(refCount == 2);
	}
	
	// ************************** Attach **************************
	{
		HRESULT hr;
		ULONG refCount;
	
		ComPtr<IDXGIFactory4> factory;
		hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory));  // S_OK
		refCount = GetRefCount(factory);                  // 1
		assert(refCount == 1);
	
		// ComPtr<IDXGIFactory4> factoryAttached;
		// factoryAttached.Attach(factory.Get());
		refCount = GetRefCount(factory);                  // 1
		assert(refCount == 1);
	
		// Compile Error! Release() 두번
	}
	
	
	// ************************** Constructor **************************
	{
		HRESULT hr;
		ULONG refCount;
	
		ComPtr<IDXGIFactory4> factory;
		hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory));            // S_OK
		refCount = GetRefCount(factory);                            // refCount = 1
	
		ComPtr<IDXGIFactory4> factory1();                           // #1
	
		ComPtr<IDXGIFactory4> factory2(nullptr);                    // #2
		refCount = GetRefCount(factory);                  	        // refCount = 1        
		assert(refCount == 1);
	
		ComPtr<IDXGIFactory4> factory3(factory.Get());             // #3      
		refCount = GetRefCount(factory);                           // refCount = 2
		assert(refCount == 2);
	
		ComPtr<IDXGIFactory4> factory4(factory);                   // #4
		refCount = GetRefCount(factory);                           // refCount = 3
		assert(refCount == 3);
	
		// ComPtr<IDXGIAdapter> adapter(factory)                   Compile Error!
		ComPtr<IDXGIFactory> factory5(factory);                    // #5
		refCount = GetRefCount(factory);                           // refCount = 4
		assert(refCount == 4);
	
		ComPtr<IDXGIFactory4> factory6(std::move(factory));        // #6
		assert(factory.Get() == nullptr);
		refCount = GetRefCount(factory3);                          // refCount = 4
		assert(refCount == 4);
	
		ComPtr<IDXGIFactory3> factory7(std::move(factory3));       // #7
		assert(factory3.Get() == nullptr);
		refCount = GetRefCount(factory4);                          // refCount = 4
		assert(refCount == 4);
	}
	
	// ************************** CopyTo **************************
	{
		HRESULT hr;
		ULONG refCount;
	
		ComPtr<IDXGIFactory4> factory;
		hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory));           // S_OK
		refCount = GetRefCount(factory);                           // refCount = 1
		assert(refCount == 1);
	
		ComPtr<IDXGIFactory4> factory1;
		factory.CopyTo(&factory1);
		refCount = GetRefCount(factory);                           // refCount = 2
		assert(refCount == 2);
	}
	
	// ************************** Detach **************************
	{
		HRESULT hr;
		ULONG refCount;
	
		ComPtr<IDXGIFactory4> factory;
		hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory));           // S_OK
		refCount = GetRefCount(factory);                           // refCount = 1
		assert(refCount == 1);
		
		ComPtr<IDXGIFactory4> factory1(factory.Detach());          // factory.Get() == nullptr이기 때문에
		                                                           // factory의 refCount를 알 수 있는 방법이 없음.
	
		refCount = GetRefCount(factory1);                          // refCount = 2
		assert(refCount == 2);
	}

	// ************************** operator& **************************
	{
		HRESULT hr;
		ULONG refCount;

		ComPtr<IDXGIFactory4> factory;
		hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory));            // S_OK
		refCount = GetRefCount(factory);                            // refCount = 1
		assert(refCount == 1);

		auto p = &factory;
		refCount = GetRefCount(factory);                            // refCount = 1
		assert(refCount == 1);
	}
	
	return 0;
}
profile
Ability is the sum of pain and anguish

0개의 댓글