DX12 Learning - Chapter4 - Init D3D

[TOC]

前言

新开一坑,开始学一下 D3D12 的接口和一些概念,以下内容均来自 Frank Luna 的 《Introduction to 3D game programming with Direct 12》,基本就是简单的抄了一遍,中文及化简大概会在没那么忙了之后补上吧

(🕊大概会补吧,学吧,学无止尽😅)

什么你说导航还没写完,可导航没写完跟我新开 D3D12 有什么关系呢 :yum:

Conception

Why go to this extra level of indirection with descriptors?

The reason is that GPU resources are essentially generic chunks of memory. Resources are kept generic so they can be used at different stages of the rendering pipeline.

What is about to View?

A view is a synonym for descriptor. The term “view” was used in previous versions of Direct3D, and it is still used in some parts of the Direct3D 12 API. We use both interchangeably in this book; for example, constant buffer view and constant buffer descriptor mean the same thing.

Descriptors have a type, and the type implies how the resource will be used. The types of descriptors we use in this book are:

  1. CBV/SRV/UAV descriptors describe constant buffers, shader resources and unordered access view resources.
  2. Sampler descriptors describe sampler resources (used in texturing).
  3. RTV descriptors describe render target resources.
  4. DSV descriptors describe depth/stencil resources.

Implementation

Steps

  1. Create the ID3D12Device using the D3D12CreateDevice function.
  2. Create an ID3D12Fence object and query descriptor sizes.
  3. Check 4X MSAA quality level support.
  4. Create the command queue, command list allocator, and main command list.
  5. Describe and create the swap chain.
  6. Create the descriptor heaps the application requires.
  7. Resize the back buffer and create a render target view to the back buffer.
  8. Create the depth/stencil buffer and its associated depth/stencil view.
  9. Set the viewport and scissor rectangles.

Details & Implementations

Create Device

// Microsoft::WRL::ComPtr<IDXGIFactory1> mdxgi_factory
CreateDXGIFactory2(NULL, IID_PPV_ARGS(&mdxgi_factory));

// Microsoft::WRL::ComPtr<ID3D12Device> md3d_device
HRESULT hardware_result = D3D12CreateDevice(
    NULL, 							// default apdapter
    D3D_FEATURE_LEVEL_11_0, 		// minimum support feature
    IID_PPV_ARGS(&md3d_device));	// output: [iid, ppDevice]

// 默认的 adpater 出问题
if(FAILED(hardwawre_result))
{
    // find adpater
    Microsoft::WRL::ComPtr<IDXGIAdpater> p_wrap_adapter;
    mdxgi_factory->EnumWrapAdapter(IID_PPV_ARGS(&p_wrap_adapter));
    D3D12CreateDevice(
        p_wrap_adapter.Get(), 
        D3D_FEATURE_LEVEL_11_0, 
        IID_PPV_ARGS(&md3d_device));    
}

As optional, the could add following codes above to enable debug layer:

// Enable D3D12 debug layer
ComPtr<ID3D12Debug> debug_controller;
D3D12GetDebugInterface(IID_PPV_ARGS(&debug_controller));
debug_controller->EnableDebugLayer();

Create Fence & Descriptor

We cache the descriptor sizes so that it is available when we need it for various descriptor types:

md3d_device->CreateFence(NULL, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence));
// Render Target View
mrtv_desc_size = md3d_device->GetDecriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
// Depth/Stencil View
mdsv_desc_size = md3d_device->GetDecriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
// Constant Buffer View
mcbv_desc_size = md3d_device->GetDecriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV);

Check MSAA Quality Support

Check the supported quality level can be done with the following method:

D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS ms_quality_levels;
ms_quality_levels.Format = m_back_buffer_format;
ms_quality_levels.SampleCount = 4;
ms_quality_levels.Flags = D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG_NONE;
ms_quality_levels.NumQualityLevels = 0;
md3dDevice->CheckFeatureSupport(
	D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS,
	&ms_quality_levels,
	sizeof(ms_quality_levels));

m4x_msaa_quality = ms_quality_levels.NumQualityLevels;
assert(m4x_msaa_quality > 0 && "Unexpected MSAA quality level.");

Create Command Queue & List

The following function shows how we create a command queue, command allocator, and command list:

D3D12_COMMAND_QUEUE_DESC queue_desc = {};
queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
queue_desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
// Microsoft::WRL::ComPtr<ID3D12CommandQueue> mcommand_queue
md3d_device->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(&mcommand_queue));

// Microsoft::WRL::ComPtr<ID3D12CommandAllocator> mdirect_cmd_list_alloc
md3d_device->CreateCommandAllocator(
	D3D12_COMMAND_LIST_TYPE_DIRECT,
	IID_PPV_ARGS(mdirect_cmd_list_alloc.GetAddressOf()));

// Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList> mcommand_list
md3d_device->CreateCommandList(
	0,
	D3D12_COMMAND_LIST_TYPE_DIRECT,
	mdirect_cmd_list_alloc.Get(), // Associated command allocator
	NULL,                         // Initial PipelineStateObject
	IID_PPV_ARGS(mcommand_list.GetAddressOf()));

// Start off in a closed state.  This is because the first time we refer 
// to the command list we will Reset it, and it needs to be closed before
// calling Reset.
mcommand_list->Close();

Describe & Create Swap Chain

Swap chain can called multiple times, it will destroy the old one before create the new one:

// Microsoft::WRL::ComPtr<IDXGISwapChain> mswap_chain
mswap_chain.Reset();

DXGI_SWAP_CHAIN_DESC sd;
sd.BufferDesc.Width = mclient_width;
sd.BufferDesc.Height = mclient_height;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;
//DXGI_FORMAT mback_buffer_format = DXGI_FORMAT_R8G8B8A8_UNORM
sd.BufferDesc.Format = mback_buffer_format;
sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
sd.SampleDesc.Count = m4x_msaa_quality ? 4 : 1;
sd.SampleDesc.Quality = m4x_msaaState ? (m4x_msaa_quality - 1) : 0;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
// const uint32 SwapChainBufferCount = 2 // double buffering
sd.BufferCount = SwapChainBufferCount;
sd.OutputWindow = mhMainWnd;
sd.Windowed = true;
sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;

// Note: Swap chain uses queue to perform flush.
mdxgi_factory->CreateSwapChain(
	mcommand_queue.Get(),
	&sd,
	mswap_chain.GetAddressOf());

Create Descriptor Heaps

We need to create the descriptor heaps to store the descriptors/views our needs:

D3D12_DESCRIPTOR_HEAP_DESC rtv_heap_desc;
rtvHeapDesc.NumDescriptors = SwapChainBufferCount;
rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
rtvHeapDesc.NodeMask = 0;
// Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> mrtv_heap
md3dDevice->CreateDescriptorHeap(
	&rtv_heap_desc, IID_PPV_ARGS(mrtv_heap.GetAddressOf()));

D3D12_DESCRIPTOR_HEAP_DESC dsv_heap_desc;
dsvHeapDesc.NumDescriptors = 1;
dsvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
dsvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
dsvHeapDesc.NodeMask = 0;
// Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> mdsv_heap
md3dDevice->CreateDescriptorHeap(
	&dsv_heap_desc, IID_PPV_ARGS(mdsv_heap.GetAddressOf()));

Create Render Target View

ComPtr<ID3D12Resource> mswap_chain_buffer[SwapChainBufferCount];
CD3DX12_CPU_DESCRIPTOR_HANDLE rtv_heap_handle(mrtv_heap->GetCPUDescriptorHandleForHeapStart());
for (UINT i = 0; i < SwapChainBufferCount; i++)
{
	// Get the ith buffer in the swap chain.
	mSwapChain->GetBuffer(
		i, IID_PPV_ARGS(&mswap_chain_buffer[i]));
	// Create an RTV to it.
	md3d_device->CreateRenderTargetView(
		mswap_chain_buffer[i].Get(), 
        nullptr,
		rtv_heap_handle);
	// Next entry in heap.
	rtv_heap_handle.Offset(1, mrtv_desc_size);
}

Create Depth/Stencil Buffer & View

The following code example shows how we create the depth/stencil texture and its corresponding depth/stencil view, firstly, create proper resource with CreateCommittedResource:

// Create the depth/stencil buffer and view.
D3D12_RESOURCE_DESC depth_stencil_desc;
depth_stencil_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
depth_stencil_desc.Alignment = 0;
depth_stencil_desc.Width = mclient_width;
depth_stencil_desc.Height = mclient_height;
depth_stencil_desc.DepthOrArraySize = 1;
depth_stencil_desc.MipLevels = 1;

// Correction 11/12/2016: SSAO chapter requires an SRV to the depth buffer to read from 
// the depth buffer.  Therefore, because we need to create two views to the same resource:
//   1. SRV format: DXGI_FORMAT_R24_UNORM_X8_TYPELESS
//   2. DSV Format: DXGI_FORMAT_D24_UNORM_S8_UINT
// we need to create the depth buffer resource with a typeless format.  
depth_stencil_desc.Format = DXGI_FORMAT_R24G8_TYPELESS;

depth_stencil_desc.SampleDesc.Count = m4x_msaa_state ? 4 : 1;
depth_stencil_desc.SampleDesc.Quality = m4x_msaa_state ? (m4x_msaa_quality - 1) : 0;
depth_stencil_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
depth_stencil_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;

D3D12_CLEAR_VALUE opt_clear;
opt_clear.Format = mdepth_stencil_format;
opt_clear.DepthStencil.Depth = 1.0f;
opt_clear.DepthStencil.Stencil = 0;
{
D3D12_HEAP_PROPERTIES heap_properties(D3D12_HEAP_TYPE_DEFAULT);
// Microsoft::WRL::ComPtr<ID3D12Resource> mdepth_stencil_buffer;
md3d_device->CreateCommittedResource(
	&heap_properties,
	D3D12_HEAP_FLAG_NONE,
	&depth_stencil_desc,
	D3D12_RESOURCE_STATE_COMMON,
	&opt_clear,
	IID_PPV_ARGS(mdepth_stencil_buffer.GetAddressOf()));
}

Bind resources to pipeline using CreateDepthStencilView:

// Create descriptor to mip level 0 of entire resource using the format of the resource.
D3D12_DEPTH_STENCIL_VIEW_DESC dsv_desc;
dsv_desc.Flags = D3D12_DSV_FLAG_NONE;
dsv_desc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
dsv_desc.Format = mdepth_stencil_format;
dsv_desc.Texture2D.MipSlice = 0;
md3d_device->CreateDepthStencilView(mdepth_stencil_buffer.Get(), &dsv_desc, DepthStencilView());

Lastly, using barrier to trasition state:

// Transition the resource from its initial state to be used as a depth buffer.
{
	D3D12_RESOURCE_BARRIER barrier_com2dw = CD3DX12_RESOURCE_BARRIER::Transition(
        mdepth_stencil_buffer.Get(),
		D3D12_RESOURCE_STATE_COMMON, 
        D3D12_RESOURCE_STATE_DEPTH_WRITE);
	mcommand_list->ResourceBarrier(1, &barrier_com2dw);
}