Search
Duplicate

D3D12_11On12 (Directx11 → Directx12)로의 전환

DX12 학습 전에 11On12를 배우는 이유

Direct3D 12(DX12) 는 매우 로우 레벨 API이기 때문에, 메모리 관리, 동기화, 리소스 상태 전환 등을 직접 처리해야 합니다.
반면, 11On12DX11 스타일의 코드를 유지하면서 DX12의 일부 기능을 활용할 수 있도록 지원하는 래퍼 API입니다.
즉, DX11의 편의성을 유지하면서 DX12를 조금씩 익힐 수 있는 단계적인 학습 방법이 될 수 있습니다.

DX12 학습 전에 11On12를 먼저 익히면 좋은 이유

DX11 스타일로 DX12의 개념을 익힐 수 있음

DX12는 모든 리소스와 명령을 직접 관리해야 하기 때문에 진입장벽이 높습니다.
11On12는 DX11과 DX12를 혼합하여 사용할 수 있어, DX11 스타일로 코딩하면서 DX12의 일부 개념을 익히는 것이 가능합니다.
DX11의 편리한 API를 사용하면서 DX12의 커맨드 큐(Command Queue)와 커맨드 리스트(Command List) 개념을 경험할 수 있습니다.

리소스와 명령 버퍼 개념을 쉽게 익힐 수 있음

DX12에서는 명령을 직접 조립하고 실행해야 하므로 동기화 및 리소스 상태 관리를 수동으로 처리해야 합니다.
11On12는 DX11의 인터페이스를 유지하면서 DX12의 리소스를 다룰 수 있도록 지원합니다.
예제: DX12에서는 리소스를 사용하려면 ResourceBarrier()를 직접 호출해야 하지만, 11On12에서는 DX11처럼 자동으로 관리됩니다.
DX12 스타일의 커맨드 큐와 스왑 체인을 다루면서, 명령 리스트와 동기화 개념을 미리 익힐 수 있습니다.

DX12의 복잡한 초기화 과정을 생략 가능

DX12에서는 GPU와 CPU의 동기화를 직접 관리해야 하며, Fence, Descriptor Heap 설정 등이 필요합니다.
11On12는 DX11의 디바이스를 사용하면서 DX12의 기능을 활용할 수 있기 때문에, 복잡한 설정을 생략하면서 DX12 개념을 익히는 것이 가능합니다.
예제: DX12에서 루트 서명(Root Signature)과 PSO(Pipeline State Object) 를 설정해야 하지만, 11On12에서는 DX11처럼 자동 설정이 가능합니다.

DX11과 DX12를 동시에 활용 가능 (레거시 코드 지원)

기존 DX11 기반 엔진을 DX12로 천천히 마이그레이션할 때 유용합니다.
DX11 렌더링 코드를 그대로 유지하면서 DX12의 멀티스레드 렌더링 성능을 향상시킬 수 있습니다.
일부 DX12 기능(예: Async Compute)을 DX11 코드에서 실험해볼 수 있습니다.

결론: DX12 학습을 쉽게 시작하는 디딤돌 역할

DX12는 난이도가 높지만, 11On12를 사용하면 DX11 스타일로 개념을 익히면서 DX12의 핵심 요소를 경험할 수 있습니다.
DX12의 명령 리스트, 리소스 상태 전환, 동기화 등의 개념을 보다 쉽게 이해할 수 있습니다.
DX11 프로젝트를 DX12로 점진적으로 전환할 때도 유용하게 활용할 수 있습니다.
완전한 DX12 프로젝트를 시작하기 전에, 11On12를 활용하여 DX12 개념을 자연스럽게 익히는 것이 효율적인 학습 방법이 될 수 있습니다!
자 그러면 MSDN 11on12를 기준으로 설명해보겠다.
불필요한 windowAPI는 한쪽에 치워두고 dx관련된 코드들 위주로 설명하겠다.
핵심적으로 봐야할 부분은 초기화부분
void D3D1211on12::OnInit() { LoadPipeline(); LoadAssets(); }
JavaScript
복사
렌더링 부분이다.
// Render the scene. void D3D1211on12::OnRender() { PIXBeginEvent(m_commandQueue.Get(), 0, L"Render 3D"); // Record all the commands we need to render the scene into the command list. PopulateCommandList(); // Execute the command list. ID3D12CommandList* ppCommandLists[] = { m_commandList.Get() }; m_commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists); PIXEndEvent(m_commandQueue.Get()); PIXBeginEvent(m_commandQueue.Get(), 0, L"Render UI"); RenderUI(); PIXEndEvent(m_commandQueue.Get()); // Present the frame. ThrowIfFailed(m_swapChain->Present(1, 0)); MoveToNextFrame(); }
JavaScript
복사
그럼 이과정에서 일어나는 작업들을 하나하나씩 분석하면서 앞으로 나아가보자. Dx11과 중복되는 부분은 가볍게 설명하고 Dx12에서 새롭게 학습해야 할 부분을 자세하게 설명하겠다. 어차피 다음 과정에서 Dx12만을 이용해서 다시 렌더링을 진행할 예정이다. API를 빨리 습득하는 방법은 항상 이야기 해왔지만 정확히 학습하는것보다 반복학습하는것이 중요하다.

1. 디버그 레이어 설정 (Debug Layer)

cpp 복사편집 #if defined(_DEBUG) ComPtr<ID3D12Debug> debugController; if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)))) { debugController->EnableDebugLayer(); dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG; d3d11DeviceFlags |= D3D11_CREATE_DEVICE_DEBUG; d2dFactoryOptions.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION; } #endif
C++
복사
디버깅 모드에서 실행되는 경우, D3D12 디버그 레이어를 활성화한다.
DXGI_CREATE_FACTORY_DEBUG: DXGI 팩토리의 디버그 모드를 활성화한다.
D3D11_CREATE_DEVICE_DEBUG: Direct3D 11 디버그 모드를 활성화한다.
D2D1_DEBUG_LEVEL_INFORMATION: Direct2D의 디버깅 정보를 출력하도록 설정한다.

2. DXGI 팩토리 생성

cpp 복사편집 ComPtr<IDXGIFactory4> factory; ThrowIfFailed(CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&factory)));
C++
복사
DXGI 팩토리를 생성한다. (DirectX 그래픽스 인터페이스 팩토리)
DXGI 팩토리는 GPU 어댑터를 열거하고, 스왑체인(Swap Chain)을 생성하는 역할을 한다.

3. Direct3D 12 장치 생성

cpp 복사편집 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_d3d12Device) )); } else { ComPtr<IDXGIAdapter1> hardwareAdapter; GetHardwareAdapter(factory.Get(), &hardwareAdapter); ThrowIfFailed(D3D12CreateDevice( hardwareAdapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_d3d12Device) )); }
C++
복사
하드웨어 GPU 사용 여부(m_useWarpDevice) 에 따라 GPU 어댑터를 선택한다.
EnumWarpAdapter(): WARP(소프트웨어 렌더러)를 사용하는 경우 DXGI 어댑터를 가져온다.
GetHardwareAdapter(): 실제 하드웨어 GPU를 가져오는 함수.
D3D12CreateDevice(): Direct3D 12 장치를 생성하며, D3D_FEATURE_LEVEL_11_0을 요구한다.

4. 디버그 메시지 필터링

cpp 복사편집 #if defined(_DEBUG) ComPtr<ID3D12InfoQueue> infoQueue; if (SUCCEEDED(m_d3d12Device->QueryInterface(IID_PPV_ARGS(&infoQueue)))) { D3D12_MESSAGE_SEVERITY severities[] = { D3D12_MESSAGE_SEVERITY_INFO, }; D3D12_MESSAGE_ID denyIds[] = { D3D12_MESSAGE_ID_INVALID_DESCRIPTOR_HANDLE, }; D3D12_INFO_QUEUE_FILTER filter = {}; filter.DenyList.NumSeverities = _countof(severities); filter.DenyList.pSeverityList = severities; filter.DenyList.NumIDs = _countof(denyIds); filter.DenyList.pIDList = denyIds; ThrowIfFailed(infoQueue->PushStorageFilter(&filter)); } #endif
C++
복사
Direct3D 12에서 발생하는 불필요한 디버그 메시지를 필터링한다.
무시할 메시지
D3D12_MESSAGE_ID_INVALID_DESCRIPTOR_HANDLE: 사용되지 않은 디스크립터 핸들 오류 방지.

5. 명령 큐 생성 (Command Queue)

// Describe and create the command queue. D3D12_COMMAND_QUEUE_DESC queueDesc = {}; queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; ThrowIfFailed(m_d3d12Device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&m_commandQueue))); NAME_D3D12_OBJECT(m_commandQueue);
C++
복사

이 코드는 Direct3D 12에서 GPU 명령을 처리하는 "명령 큐(Command Queue)"를 생성하는 과정이다.

Direct3D 12에서는 GPU가 실행할 작업(명령 리스트)을 명령 큐(Command Queue)에 제출하여 비동기적으로 실행한다.

1. 명령 큐(Command Queue)란?

Direct3D 12에서 GPU 작업을 관리하는 핵심 구조 중 하나로, CPU가 GPU에 명령을 보내는 통로다.
Direct3D 12는 명령 리스트(Command List) 기반 구조이므로, GPU가 직접 실행하는 명령을 담아 명령 큐에 추가해야 한다.
GPU는 명령 큐에서 명령 리스트를 가져와 실행한다.
CPU와 GPU의 병렬 처리를 효율적으로 관리할 수 있다.

2. 코드 상세 분석

(1) 명령 큐 속성 설정

D3D12_COMMAND_QUEUE_DESC queueDesc = {}; queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
C++
복사
D3D12_COMMAND_QUEUE_DESC 구조체는 명령 큐의 속성을 정의하는 구조체다.
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
플래그 없음 (기본 명령 큐 설정)
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
D3D12_COMMAND_LIST_TYPE_DIRECT: GPU가 실제 그리기(Rendering) 및 컴퓨팅 작업을 수행할 수 있는 명령을 실행하는 가장 일반적인 유형.
명령 리스트 타입
설명
D3D12_COMMAND_LIST_TYPE_DIRECT
렌더링 & 그래픽 명령을 실행 (주로 사용)
D3D12_COMMAND_LIST_TYPE_COMPUTE
GPU에서 컴퓨트 쉐이더만 실행
D3D12_COMMAND_LIST_TYPE_COPY
리소스 복사 작업 전용

(2) 명령 큐 생성

ThrowIfFailed(m_d3d12Device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&m_commandQueue)));
C++
복사
CreateCommandQueue(): Direct3D 12 장치(m_d3d12Device)에서 명령 큐를 생성.
m_commandQueueGPU가 사용할 명령 큐가 저장됨.
ThrowIfFailed(...) → DirectX 함수 실행 결과를 확인하고 오류 발생 시 예외를 던지는 함수.

(3) 명령 큐에 이름 할당

NAME_D3D12_OBJECT(m_commandQueue);
C++
복사
NAME_D3D12_OBJECT()디버깅을 위해 Direct3D 12 오브젝트에 이름을 지정하는 함수.
디버깅 도구(예: PIX, RenderDoc)에서 명령 큐를 추적할 때 가독성을 높이기 위해 사용.

3. 명령 큐의 역할

CPU가 명령 리스트를 GPU에 제출하는 역할.
여러 개의 명령 리스트(Command List)를 큐에 넣으면 GPU가 비동기적으로 처리.
여러 개의 명령 큐를 생성하여 그래픽, 컴퓨트, 복사 작업을 병렬로 실행할 수도 있음.

4. 정리

명령 큐(Command Queue)는 Direct3D 12에서 GPU 명령을 처리하는 핵심 요소.
D3D12_COMMAND_LIST_TYPE_DIRECT를 사용하여 렌더링 작업을 실행할 수 있도록 설정.
CreateCommandQueue()를 호출하여 GPU에서 실행할 명령을 대기열에 추가할 수 있도록 함.
CPU가 명령 리스트를 생성하고, 이 리스트를 명령 큐에 추가하면 GPU가 실행하는 방식.

결론: 이 코드가 하는 일

Direct3D 12 명령 큐를 생성하여 GPU가 명령을 실행할 준비를 완료!
이후 명령 리스트(Command List)를 생성하고 이를 큐에 제출하면 GPU가 해당 작업을 실행하게 됨.

6. 스왑체인(Swap Chain) 생성

DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; swapChainDesc.BufferCount = FrameCount; swapChainDesc.Width = m_width; swapChainDesc.Height = m_height; swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; swapChainDesc.SampleDesc.Count = 1; ComPtr<IDXGISwapChain1> swapChain; ThrowIfFailed(factory->CreateSwapChainForHwnd( m_commandQueue.Get(), Win32Application::GetHwnd(), &swapChainDesc, nullptr, nullptr, &swapChain )); ThrowIfFailed(factory->MakeWindowAssociation(Win32Application::GetHwnd(), DXGI_MWA_NO_ALT_ENTER)); ThrowIfFailed(swapChain.As(&m_swapChain)); m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
C++
복사
스왑 체인은 백 버퍼와 프론트 버퍼를 교환하여 화면을 렌더링하는 메커니즘.
DXGI_SWAP_EFFECT_FLIP_DISCARD: 가장 효율적인 프레임 버퍼 전환 방식.
m_frameIndex: 현재 백 버퍼의 인덱스 저장.

7. Direct3D 11On12 장치 생성

ComPtr<ID3D11Device> d3d11Device; ThrowIfFailed(D3D11On12CreateDevice( m_d3d12Device.Get(), d3d11DeviceFlags, nullptr, 0, reinterpret_cast<IUnknown**>(m_commandQueue.GetAddressOf()), 1, 0, &d3d11Device, &m_d3d11DeviceContext, nullptr )); ThrowIfFailed(d3d11Device.As(&m_d3d11On12Device));
C++
복사
D3D11On12CreateDevice(): D3D12 장치 위에 D3D11 장치를 래핑하여 생성.
m_d3d11On12Device: Direct3D 11 API를 사용하여 Direct3D 12 리소스를 조작할 수 있도록 함.

8. Direct2D 및 DirectWrite 초기화

ThrowIfFailed(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory3), &d2dFactoryOptions, &m_d2dFactory)); ComPtr<IDXGIDevice> dxgiDevice; ThrowIfFailed(m_d3d11On12Device.As(&dxgiDevice)); ThrowIfFailed(m_d2dFactory->CreateDevice(dxgiDevice.Get(), &m_d2dDevice)); ThrowIfFailed(m_d2dDevice->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &m_d2dDeviceContext)); ThrowIfFailed(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), &m_dWriteFactory));
C++
복사
Direct2D 및 DirectWrite 생성: 2D 그래픽스와 텍스트 렌더링을 위해 초기화.

9. 데스크톱 DPI 설정 가져오기

float dpiX; float dpiY; #pragma warning(push) #pragma warning(disable : 4996) // GetDesktopDpi is deprecated. m_d2dFactory->GetDesktopDpi(&dpiX, &dpiY); #pragma warning(pop) D2D1_BITMAP_PROPERTIES1 bitmapProperties = D2D1::BitmapProperties1( D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW, D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED), dpiX, dpiY );
C++
복사
GetDesktopDpi()는 Direct2D 팩토리를 통해 시스템 DPI 정보를 가져오는 함수.
하지만 이 함수는 Windows 10 이후로 deprecated(사용 중단)되었기 때문에, 경고를 비활성화하고 사용한다.
dpiX, dpiY 값은 Direct2D 비트맵을 생성할 때 사용된다.
// Create descriptor heaps. { // Describe and create a render target view (RTV) descriptor heap. D3D12_DESCRIPTOR_HEAP_DESCrtvHeapDesc = {}; rtvHeapDesc.NumDescriptors = FrameCount; rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; ThrowIfFailed(m_d3d12Device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&m_rtvHeap))); m_rtvDescriptorSize = m_d3d12Device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); }
C++
복사

1. 디스크립터 힙(Descriptor Heap)이란?

Direct3D 12에서 디스크립터 힙은 GPU 리소스를 관리하는 메모리 구조다.
여러 개의 RTV, DSV(Depth Stencil View), SRV(Shader Resource View), UAV(Unordered Access View) 등의 디스크립터를 저장할 수 있다.
Direct3D 12는 각 리소스를 개별적으로 바인딩하지 않고, 디스크립터 힙에서 한 번에 참조하도록 설계됨.
따라서 디스크립터 힙을 잘 구성하는 것이 성능 최적화의 핵심이다.

2. 코드 상세 분석

(1) RTV 디스크립터 힙 설명 (Descriptor Heap Description)

D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {}; // RTV 힙 구조체 초기화 rtvHeapDesc.NumDescriptors = FrameCount; // 프레임 수만큼 디스크립터 할당 rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; // RTV(렌더 타겟 뷰) 타입 rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; // 플래그 없음 (RTV는 CPU에서만 사용됨)
C++
복사
D3D12_DESCRIPTOR_HEAP_DESC디스크립터 힙의 속성을 정의하는 구조체이다.
NumDescriptors = FrameCount프레임 수만큼 디스크립터를 생성(일반적으로 스왑 체인의 백 버퍼 수)
Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV렌더 타겟 뷰(RTV)용 디스크립터 힙을 생성
Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE → RTV는 GPU에서 바인딩하는 방식이 아니라 CPU에서 관리하므로 추가 플래그가 필요 없음.

(2) RTV 디스크립터 힙 생성

ThrowIfFailed(m_d3d12Device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&m_rtvHeap)));
C++
복사
CreateDescriptorHeap()를 호출하여 RTV 디스크립터 힙을 생성하고 m_rtvHeap에 저장.
RTV 힙은 GPU에서 접근할 필요가 없으므로 특별한 설정이 필요하지 않음.

(3) RTV 디스크립터 크기 저장

m_rtvDescriptorSize = m_d3d12Device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
C++
복사
RTV 디스크립터 힙의 디스크립터 크기를 저장하여 다음 RTV 핸들 생성 시 사용.
GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV)RTV 디스크립터 크기를 가져옴 (디스크립터 간의 오프셋을 계산하는 데 필요)

3. RTV 디스크립터 힙이 필요한 이유

Direct3D 12에서는 모든 렌더 타겟(RTV), 심도-스텐실 뷰(DSV), 셰이더 리소스(SRV) 등을 디스크립터 힙에 저장해야 한다.
렌더 타겟을 사용하려면 RTV 디스크립터 힙이 필수이며, 이 힙은 GPU가 사용하는 백 버퍼(Back Buffer)를 관리하는 데 사용된다.

4. 정리

RTV 디스크립터 힙을 생성하여 GPU가 여러 프레임을 관리할 수 있도록 설정.
프레임 수(FrameCount)만큼 RTV 디스크립터를 할당하여 스왑 체인과 연결.
RTV 디스크립터 크기를 저장하여 이후 핸들 이동 시 활용.
Direct3D 12에서는 디스크립터 힙을 통해 리소스를 관리해야 하므로 필수적인 작업.
이제 렌더 타겟 뷰(RTV)를 설정하고 백 버퍼를 렌더링 대상으로 활용할 준비가 완료됨.
// Create frame resources. { CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_rtvHeap->GetCPUDescriptorHandleForHeapStart()); // Create a RTV, D2D render target, and a command allocator for each frame. for (UINT n = 0; n < FrameCount; n++) { ThrowIfFailed(m_swapChain->GetBuffer(n, IID_PPV_ARGS(&m_renderTargets[n]))); m_d3d12Device->CreateRenderTargetView(m_renderTargets[n].Get(), nullptr, rtvHandle); NAME_D3D12_OBJECT_INDEXED(m_renderTargets, n); // Create a wrapped 11On12 resource of this back buffer. Since we are // rendering all D3D12 content first and then all D2D content, we specify // the In resource state as RENDER_TARGET - because D3D12 will have last // used it in this state - and the Out resource state as PRESENT. When // ReleaseWrappedResources() is called on the 11On12 device, the resource // will be transitioned to the PRESENT state. D3D11_RESOURCE_FLAGS d3d11Flags = { D3D11_BIND_RENDER_TARGET }; ThrowIfFailed(m_d3d11On12Device->CreateWrappedResource( m_renderTargets[n].Get(), &d3d11Flags, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT, IID_PPV_ARGS(&m_wrappedBackBuffers[n]) )); // Create a render target for D2D to draw directly to this back buffer. ComPtr<IDXGISurface> surface; ThrowIfFailed(m_wrappedBackBuffers[n].As(&surface)); ThrowIfFailed(m_d2dDeviceContext->CreateBitmapFromDxgiSurface( surface.Get(), &bitmapProperties, &m_d2dRenderTargets[n] )); rtvHandle.Offset(1, m_rtvDescriptorSize); ThrowIfFailed(m_d3d12Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&m_commandAllocators[n]))); } }
C++
복사

1. 프레임 리소스란?

Direct3D 12에서는 **멀티 버퍼링(Multi-buffering)**을 통해 성능을 최적화한다.
즉, 여러 개의 백 버퍼(Back Buffer)를 번갈아 가며 사용하여 렌더링 속도를 높인다.
이 코드는 각 프레임마다 렌더 타겟, Direct2D 비트맵, 그리고 명령 할당자(Command Allocator)를 생성하는 역할을 한다.

2. 코드 상세 분석

(1) RTV 디스크립터 핸들 생성

CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_rtvHeap->GetCPUDescriptorHandleForHeapStart());
C++
복사
CD3DX12_CPU_DESCRIPTOR_HANDLE디스크립터 힙에서 특정 RTV 핸들을 가져오는 헬퍼 클래스.
m_rtvHeap->GetCPUDescriptorHandleForHeapStart() → RTV 디스크립터 힙의 첫 번째 핸들을 가져옴.
이후 rtvHandle.Offset(1, m_rtvDescriptorSize);를 통해 다음 핸들로 이동.

(2) 각 프레임의 렌더 타겟(RTV) 설정

for (UINT n = 0; n < FrameCount; n++) { ThrowIfFailed(m_swapChain->GetBuffer(n, IID_PPV_ARGS(&m_renderTargets[n]))); m_d3d12Device->CreateRenderTargetView(m_renderTargets[n].Get(), nullptr, rtvHandle); ...
C++
복사
각 프레임의 백 버퍼(Back Buffer)를 가져와 RTV로 설정.
m_swapChain->GetBuffer(n, IID_PPV_ARGS(&m_renderTargets[n]))
스왑 체인에서 n번째 프레임의 백 버퍼를 가져옴.
m_d3d12Device->CreateRenderTargetView(...)
Direct3D 12 장치에서 해당 백 버퍼를 렌더 타겟 뷰(RTV)로 설정.
각 프레임마다 다른 백 버퍼를 사용하여 더블 버퍼링 또는 트리플 버퍼링을 구현.

(3) Direct3D 11On12 래핑

cpp 복사편집 D3D11_RESOURCE_FLAGS d3d11Flags = { D3D11_BIND_RENDER_TARGET }; ThrowIfFailed(m_d3d11On12Device->CreateWrappedResource( m_renderTargets[n].Get(), &d3d11Flags, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT, IID_PPV_ARGS(&m_wrappedBackBuffers[n]) ));
C++
복사
D3D12의 백 버퍼를 D3D11에서도 사용할 수 있도록 래핑.
D3D11_RESOURCE_FLAGS d3d11Flags = { D3D11_BIND_RENDER_TARGET };
Direct3D 11에서 렌더 타겟으로 사용할 수 있도록 설정.
D3D12_RESOURCE_STATE_RENDER_TARGET
D3D12에서 사용할 때는 렌더 타겟(RTV) 상태로 설정.
D3D12_RESOURCE_STATE_PRESENT
D3D11이 사용할 때는 PRESENT 상태로 설정.
이후 ReleaseWrappedResources()를 호출하면, 리소스가 자동으로 PRESENT 상태로 전환.

(4) Direct2D 렌더 타겟 생성

cpp 복사편집 ComPtr<IDXGISurface> surface; ThrowIfFailed(m_wrappedBackBuffers[n].As(&surface)); ThrowIfFailed(m_d2dDeviceContext->CreateBitmapFromDxgiSurface( surface.Get(), &bitmapProperties, &m_d2dRenderTargets[n] ));
C++
복사
m_wrappedBackBuffers[n].As(&surface)
DXGI 서피스로 변환하여 Direct2D에서도 접근 가능하게 만듦.
CreateBitmapFromDxgiSurface()
DXGI 서피스를 Direct2D 비트맵으로 변환하여, Direct2D에서 그릴 수 있도록 설정.
즉, Direct3D 12에서 생성한 백 버퍼를 Direct2D에서 사용할 수 있도록 변환하는 과정이다.

(5) 디스크립터 핸들 이동

rtvHandle.Offset(1, m_rtvDescriptorSize);
C++
복사
다음 프레임(리소스)의 RTV 핸들로 이동.
m_rtvDescriptorSize만큼 오프셋을 증가시켜 다음 백 버퍼를 처리할 수 있도록 설정.

(6) 각 프레임의 명령 할당자(Command Allocator) 생성

cpp 복사편집 ThrowIfFailed(m_d3d12Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&m_commandAllocators[n])));
C++
복사
프레임 마다 독립적인 커맨드 할당자(Command Allocator)를 생성.
D3D12_COMMAND_LIST_TYPE_DIRECT
GPU가 직접 실행할 수 있는 명령을 저장할 수 있도록 설정.
각 프레임이 독립적인 커맨드 할당자를 가지므로, 프레임 간 충돌 없이 명령을 실행 가능.

3. 프레임 리소스의 역할

이 코드의 목적은 멀티 프레임을 효율적으로 관리할 수 있도록 설정하는 것이다.
Direct3D 12의 백 버퍼를 RTV로 설정 (렌더 타겟 뷰)
D3D11On12 래핑을 사용하여 Direct3D 11에서도 사용 가능하게 변환
DXGI 서피스를 이용해 Direct2D에서 그릴 수 있도록 설정
각 프레임마다 독립적인 명령 할당자(Command Allocator)를 생성하여 병렬 실행 가능

4. 정리

Direct3D 12 + Direct3D 11On12 + Direct2D 통합 렌더링 설정

Direct3D 12의 백 버퍼(Back Buffer)를 가져와 렌더 타겟(RTV)으로 설정.
Direct3D 11에서도 접근할 수 있도록 11On12 래핑을 수행.
Direct2D에서도 렌더링할 수 있도록 DXGI 서피스를 이용해 D2D 비트맵을 생성.
프레임마다 독립적인 명령 할당자(Command Allocator)를 생성하여 충돌 없이 실행 가능.

결론: 이 코드가 하는 일

각 프레임에 대한 렌더 타겟을 설정하여 멀티 프레임 렌더링을 지원!
Direct3D 12 백 버퍼를 Direct3D 11 및 Direct2D에서 사용할 수 있도록 변환!
각 프레임이 독립적으로 실행될 수 있도록 명령 할당자를 생성하여 성능 최적화!
즉, Direct3D 12와 Direct3D 11On12, 그리고 Direct2D를 통합하여 동일한 백 버퍼를 공유할 수 있도록 설정하는 과정이다.