Search
Duplicate

Initalize the API(directX 12)

Directx12 를 사용하려면 GPU에 접근하는 데이터들을 정확한 구조로 만들어줘야 한다.
D3D12_YamYamCoding
eazuooz

Factory

팩토리는 DirectX 12 API의 진입점입니다. 팩토리는 디바이스 및 기타 중요한 데이터 구조를 생성할 수 있는 어댑터를 찾는 데 사용할 수 있습니다.
// 👋 Declare DirectX 12 Handles IDXGIFactory4* factory; ID3D12Debug1* debugController; // 🏭 Create Factory UINT dxgiFactoryFlags = 0; #if defined(_DEBUG) // 🐛 Create a Debug Controller to track errors ID3D12Debug* dc; ThrowIfFailed(D3D12GetDebugInterface(IID_PPV_ARGS(&dc))); ThrowIfFailed(dc->QueryInterface(IID_PPV_ARGS(&debugController))); debugController->EnableDebugLayer(); debugController->SetEnableGPUBasedValidation(true); dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG; dc->Release(); dc = nullptr; #endif HRESULT result = CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&factory));
C++
복사

Adapter

어댑터는 여러분의 GPU 하드웨어 속성을 제공하는 장치입니다. GPU의 이름, 제조업체, 메모리 크기 등 여러가지 정보에 접근가능하게 도와줍니다.
어댑터에는 2가지 종류가 있습니다. 소프트웨어 어댑터, 하드웨어 어댑터 입니다. 마이크로소프트는 항상 소프트웨어 어댑터를 구현해왔습니다. 이는 GPU가 없는 PC에서도실행이 가능하게끔 지원해줍니다.
// 👋 Declare Handles IDXGIAdapter1* adapter; // 🔌 Create Adapter for (UINT adapterIndex = 0; DXGI_ERROR_NOT_FOUND != factory->EnumAdapters1(adapterIndex, &adapter); ++adapterIndex) { DXGI_ADAPTER_DESC1 desc; adapter->GetDesc1(&desc); // ❌ Don't select the Basic Render Driver adapter. if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) { continue; } // ✔️ Check if the adapter supports Direct3D 12, and use that for the rest // of the application if (SUCCEEDED(D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_12_0, _uuidof(ID3D12Device), nullptr))) { break; } // ❌ Else we won't use this iteration's adapter, so release it adapter->Release(); }
C++
복사

Device

Device를 사용하여 Command Queue, Allocators, gpu-resources, pipe-line, buffers, buffer views, shader blobs, heaps, 동기화 요소들 과같은 데이터구조들을 만들수 있다.
// 👋 Declare Handles ID3D12Device* device; // 💻 Create Device ID3D12Device* pDev = nullptr; ThrowIfFailed(D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&device)));
C++
복사
디버그 디바이스를 만들엇 사용하면 그래픽 디버그 모드를 사용 할수도 있습니다. 이를 통해서 그래픽 작업 관련해서 잘못 된 점들을 찾아서 고칠수 있습니다.
// 👋 Declare Handles ID3D12DebugDevice* debugDevice; #if defined(_DEBUG) // 💻 Get debug device ThrowIfFailed(device->QueryInterface(&debugDevice)); #endif
C++
복사

Command Queue

Command 큐는 커맨드(명령)들을 제출하는 곳입니다. Command list 라는 그룹을 커맨드큐에 제출합니다. 커맨드 큐를 사용해서 명령들을 따로 관리하면 GPU는 계속 바쁘게 일을하고 GPU가 일을하는것과 상관없이 큐에 들어온 명령들을 차례대로 실행하면 됩니다. 예를 들어 버퍼를 만들어 GPU에 제출하고 보내는 동안 레스터라이제이션 파이프라인, 컴퓨트 파이프라인은 계속 일을 하고 있을수 있습니다.
보통은 1개로 사용하지만 그래픽스 렌더링 구조에 따라 최대 3가지 타입의 큐로 나눠서 사용할 수 있습니다. Direct3D 12에서 커맨드 큐의 종류
타입
설명
주 사용 용도
D3D12_COMMAND_LIST_TYPE_DIRECT
가장 일반적인 큐
그래픽 명령, 드로우콜, 리소스 복사 등
D3D12_COMMAND_LIST_TYPE_COMPUTE
GPU 연산 전용
딥러닝, 파티클, GPGPU 계산 등
D3D12_COMMAND_LIST_TYPE_COPY
리소스 복사 전용
GPU 간, CPU→GPU 등 리소스 업로드
// 👋 Declare Handles ID3D12CommandQueue* commandQueue; // 📦 Create Command Queue D3D12_COMMAND_QUEUE_DESC queueDesc = {}; queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; ThrowIfFailed(device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&commandQueue)));
C++
복사

Command Allocator

Command allocator 를 사용하여 GPU에 제출할 수 있는 command list 를 만들 수 있습니다.
// 👋 Declare Handles ID3D12CommandAllocator* commandAllocator; // 🎅 Create Command Allocator ThrowIfFailed(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&commandAllocator)));
C++
복사

Synchronization

Dx12 에서는 여러개의 커맨드 큐를 병렬로 처리하기 때문에 동기화 작업이 필요합니다. 펜스는 GPU 메모리를 사용하고 할당하고 삭제 할때 특정작업 관련된걸 GPU에게 알려주는 역할을 합니다. 일종의 동기화를 하기위해 알려준다고 생각하면 됩니다.
// 👋 Declare handles UINT frameIndex; HANDLE fenceEvent; ID3D12Fence* fence; UINT64 fenceValue; // 🚧 Create fence ThrowIfFailed(device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)));
C++
복사
Barrier 는 리소스를 어떻게 사용해야 할지 알려줍니다. 예를 들어 텍스처를 컴퓨트 쉐이더용으로 사용하고 있는데 다른곳에서 렌더타겟으로 사용하는걸 방지 합니다. 기본적으로 리소스의 사용을 Barrier를 사용하여 어떤 리소스로 사용할지 항상 명시해야 합니다.
// 👋 Declare handles ID3D12GraphicsCommandList* commandList; // 🔮 Create Barrier D3D12_RESOURCE_BARRIER barrier = {}; result.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; result.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; barrier.Transition.pResource = texResource; barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE; barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_UNORDERED_ACCESS; barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; commandList->ResourceBarrier(1, &barrier);
C++
복사
Direct3D 12에서는 리소스가 "동시에 여러 상태로 사용되는 것"은 허용되지 않아.
즉, GPU 파이프라인에서 동일한 리소스를 다른 상태로 동시에 접근하면 안 돼.
→ 반드시 상태 전환(ResourceBarrier)이 필요해.

Swapchain

스왑체인은 백버퍼 (frame buffers)를 할당하고 백버퍼에 그린 그림을 모니터에 그려줍니다.
// 💾 Declare Data unsigned width = 640; unsigned height = 640; // 👋 Declare Handles static const UINT backbufferCount = 2; UINT currentBuffer; ID3D12DescriptorHeap* renderTargetViewHeap; ID3D12Resource* renderTargets[backbufferCount]; UINT rtvDescriptorSize; // ⛓️ Swapchain IDXGISwapChain3* swapchain; D3D12_VIEWPORT viewport; D3D12_RECT surfaceSize; surfaceSize.left = 0; surfaceSize.top = 0; surfaceSize.right = static_cast<LONG>(width); surfaceSize.bottom = static_cast<LONG>(height); viewport.TopLeftX = 0.0f; viewport.TopLeftY = 0.0f; viewport.Width = static_cast<float>(width); viewport.Height = static_cast<float>(height); viewport.MinDepth = .1f; viewport.MaxDepth = 1000.f; if (swapchain != nullptr) { // Create Render Target Attachments from swapchain swapchain->ResizeBuffers(backbufferCount, width, height, DXGI_FORMAT_R8G8B8A8_UNORM, 0); } else { // ⛓️ Create swapchain DXGI_SWAP_CHAIN_DESC1 swapchainDesc = {}; swapchainDesc.BufferCount = backbufferCount; swapchainDesc.Width = width; swapchainDesc.Height = 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; IDXGISwapChain1* newSwapchain = xgfx::createSwapchain(window, factory, commandQueue, &swapchainDesc); HRESULT swapchainSupport = swapchain->QueryInterface( __uuidof(IDXGISwapChain3), (void**)&newSwapchain); if (SUCCEEDED(swapchainSupport)) { swapchain = (IDXGISwapChain3*)newSwapchain; } } frameIndex = swapchain->GetCurrentBackBufferIndex(); // Describe and create a render target view (RTV) descriptor heap. D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {}; rtvHeapDesc.NumDescriptors = backbufferCount; rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; ThrowIfFailed(device->CreateDescriptorHeap( &rtvHeapDesc, IID_PPV_ARGS(&renderTargetViewHeap))); rtvDescriptorSize = device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); // 🎞️ Create frame resources D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle(renderTargetViewHeap->GetCPUDescriptorHandleForHeapStart()); // Create a RTV for each frame. for (UINT n = 0; n < backbufferCount; n++) { ThrowIfFailed(swapchain->GetBuffer(n, IID_PPV_ARGS(&renderTargets[n]))); device->CreateRenderTargetView(renderTargets[n], nullptr, rtvHandle); rtvHandle.ptr += (1 * rtvDescriptorSize); }
C++
복사