Notice
Recent Posts
Recent Comments
Link
«   2026/02   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
Tags
more
Archives
Today
Total
관리 메뉴

MOONSUN

[D3D] Comptr(COM Smart Pointer) 란? 본문

D3D

[D3D] Comptr(COM Smart Pointer) 란?

MoonSun_v 2025. 9. 5. 11:43

 

 

DirectX에서 자주 보게 되는 Comptr 은 스마트 포인터(Smart Pointer)의 일종으로,
COM(Component Object Model) 객체를 안전하게 관리하기 위해 사용된다.

 

 

COM(Component Object Model) 객체?

DirectX의 대부분 리소스(ID3D11Device, ID3D11Buffer, IDXGISwapChain 등)는 COM 객체다. 

 

ComPtr ?

DirectX에서 ComPtr은 COM 객체 관리용 스마트 포인터로,

Release()를 직접 관리하지 않아도 되고 예외 상황에서도 안전하게 리소스를 관리할 수 있다.

 

 

 

1. 왜 ComPtr을 쓸까?

COM 객체는 참조 카운트(reference count) 방식으로 메모리를 관리한다.

  • 객체를 생성하면 참조 카운트가 1
  • AddRef() 호출 시 카운트 증가
  • Release() 호출 시 카운트 감소
  • 카운트가 0이 되면 객체가 메모리에서 해제됨

하지만 매번 AddRef, Release를 직접 호출하면 실수하기 쉽고, 메모리 누수(leak)나 해제 후 접근(crash)이 발생할 수 있기 때문에, ComPtr이 자동으로 참조 카운트를 관리해 줍니다.

 

 

 

2. ComPtr 정의

Comptr은 WRL(Windows Runtime Library)에 들어있는 템플릿 클래스다.
#include <wrl/client.h>
using Microsoft::WRL::ComPtr;

 

 

 

3. 사용 예시

3-1. 기본적인 사용

ComPtr<ID3D11Device> m_pDevice;
ComPtr<ID3D11DeviceContext> m_pDeviceContext;
ComPtr<IDXGISwapChain> m_pSwapChain;
ComPtr<ID3D11RenderTargetView> m_pRenderTargetView;
    
...
    
// 디바이스, 스왑체인, 컨텍스트 생성
HR_T(D3D11CreateDeviceAndSwapChain(
	nullptr,						// 기본 어댑터 사용
	D3D_DRIVER_TYPE_HARDWARE,		// 하드웨어 렌더링 사용
	nullptr,						// 소프트웨어 드라이버 없음
	creationFlags,					// 디버그 플래그
	nullptr, 0,						// 기본 Feature Level 사용
	D3D11_SDK_VERSION,				// SDK 버전
	&swapDesc,						// 스왑체인 설명 구조체
	m_pSwapChain.GetAddressOf(),	// 스왑체인 반환
	m_pDevice.GetAddressOf(),		// 디바이스 반환
	nullptr,						// Feature Level 반환 (사용 안 함)
	m_pDeviceContext.GetAddressOf()	// 디바이스 컨텍스트 반환
));

위는 내가 사용 중인 코드의 일부분이다.

-> m_pDevice, m_pDeviceContext, m_pSwapChain 모두 스코프를 벗어나면 자동으로 Release() 호출된다.

 

( D3D11CreateDeviceAndSwapChain은 내부적으로

COM 객체(ID3D11Device, ID3D11DeviceContext, IDXGISwapChain 등) 를 생성하고, 각 객체의 참조 카운트를 AddRef 한 상태로 반환한다. )

 

 

3-2. 직접 Release() 불필요 

{
    ComPtr<ID3D11Buffer> vertexBuffer;

    // vertexBuffer 초기화 코드...

} // 여기서 자동으로 Release() 호출됨

 

 

3-3. 포인터 접근

  • 원본 포인터가 필요할 때는 Get() 사용
  • 새로운 객체를 받아야 할 때는 ReleaseAndGetAddressOf() 또는 GetAddressOf() 사용
ComPtr<ID3D11Texture2D> texture;

// 함수에 포인터를 넘길 때
device->CreateTexture2D(&desc, nullptr, texture.GetAddressOf());

// 이미 있는 것을 덮어쓸 때 (기존 객체 Release 후 새로 바인딩)
device->CreateTexture2D(&desc, nullptr, texture.ReleaseAndGetAddressOf());

 

 

 

4. Comptr 장점

장점을 정리해보자면.

  • 자동 AddRef/Release 관리 → 메모리 누수 방지
  • nullptr 대입, Reset()으로 명확히 해제 가능
  • 예외 발생 시에도 안전하게 해제됨
  • 일반 포인터와 호환 (Get()/GetAddressOf() 활용)

 

5. Reset()

 

  • ComPtr은 스코프 종료 시 자동 해제
  • Reset()은 스코프가 끝나기 전, 내가 원하는 타이밍에 해제
  • 보통 "리소스 교체, 씬 전환 시점, 임시 리소스 해제" 등에 사용

 

 

 

5-1. 중간에 리소스 교체할 때

// 기존 버퍼 해제 후 새 버퍼로 교체
m_pVertexBuffer.Reset();
HR(m_pDevice->CreateBuffer(&desc, &data, m_pVertexBuffer.GetAddressOf()));

 

  • Reset()을 안 하면 이전 버퍼가 그대로 붙어 있어서 메모리 릭 발생 가능
  • Reset()으로 기존 리소스를 Release → nullptr로 초기화

 

 

5-2. 수명 명확히 제어할 때

void Render()
{
    ComPtr<ID3D11Texture2D> tempTex;
    m_pSwapChain->GetBuffer(0, IID_PPV_ARGS(&tempTex));

    // tempTex 사용해서 RTV 생성
    m_pDevice->CreateRenderTargetView(tempTex.Get(), nullptr, m_pRTV.GetAddressOf());

    // 여기서 Reset() 하면 tempTex는 바로 해제됨 (RTV만 살아있음)
    tempTex.Reset();
}
  • RTV를 만든 뒤 더 이상 backbuffer 텍스처가 필요 없다면 즉시 해제 가능

 

5-3. 메모리 절약(명시적 해제)

void UninitScene()
{
    m_pVertexBuffer.Reset();
    m_pIndexBuffer.Reset();
    m_pInputLayout.Reset();
    m_pVertexShader.Reset();
    m_pPixelShader.Reset();
}
  • 소멸자까지 기다리지 않고 씬이 끝나는 즉시 리소스 반환

 

5-4. 포인터 재활용 전 안전 초기화 

m_pShader.Reset();   // 기존 셰이더 해제
CompileShaderFromFile(L"NewShader.hlsl", "main", "vs_5_0", blob.GetAddressOf());
m_pDevice->CreateVertexShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, m_pShader.GetAddressOf());
  • 새 리소스를 넣기 전에 항상 Reset() 해주면 덮어쓰기 전 누수 방지

 

 

6. RAW 포인터 vs Comptr

우리가 일반적으로 알고있는 포인터와 Comptr을 간단하게 정리 해보자면.

 

RAW 포인터

ID3D11Buffer* buffer = nullptr;
device->CreateBuffer(&desc, nullptr, &buffer);

// 해제 안 하면 메모리 누수 발생
buffer->Release();

 

Comptr

ComPtr<ID3D11Buffer> buffer;
device->CreateBuffer(&desc, nullptr, buffer.GetAddressOf());
// 해제 필요 없음 (스코프 종료 시 자동으로 Release)

 

 

 

 

 

 


이후에는 Comptr 활용 예시와 Smartpointer와 Comptr의 차이점에 대해 간단하게 정리해볼까.. 한다.....