MOONSUN
[D3D] Comptr(COM Smart Pointer) 란? 본문
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 정의
#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의 차이점에 대해 간단하게 정리해볼까.. 한다.....
'D3D' 카테고리의 다른 글
| [D3D 이론] 기저벡터(Basis Vector)와 행렬(Matrix) (0) | 2025.09.10 |
|---|---|
| [D3D] DirectX 11에서 HLSL 셰이더 컴파일 2가지 방법 (0) | 2025.09.05 |
| [D3D] Comptr 사용해보자 (0) | 2025.09.05 |
| [Error] Invalid vs_2_0 output semantic 'SV_TARGET’ (0) | 2025.09.04 |
| [D3D 이론] 렌더링 파이프라인에서 각 스테이지의 역할 (0) | 2025.09.04 |