目录
23.创建管道状态对象(pipeline state object)
基本组件的创建和绘图流程
1.头文件
| #include <d3d12.h> | 最基本的dx12头文件 |
| #include "d3dx12.h" | d3dx12与以往的不同,现在只有一个头文件,去官方示例里复制一份即可 |
| #include <dxgi1_6.h> | dxgi相关 |
| #include <D3DCompiler.h> | 着色器编译相关 |
| #include <DirectXMath.h> | 数学库 |
2.类型名字
使用DirectX命名空间,然后typedef一些DXGI的类型,方便以后替换。
using namespace DirectX;
typedef IDXGIFactory7 _IDXGIFactory;
typedef IDXGIAdapter4 _IDXGIAdapter;
typedef IDXGISwapChain4 _IDXGISwapChain;
3.检测XMath支持
if (!XMVerifyCPUSupport())
{
dnd_debug(DL::DAMAGE, L"DirectXMath不支持!");
return;
}
4.开启调试层
开启调试层后,在出现错误时,被DX库检测到,就会输出原因。
#if defined(_DEBUG)
// Enable the D3D12 debug layer.
com_ptr<ID3D12Debug> debugController;
hr = D3D12GetDebugInterface(IID_PPV_ARGS(&debugController));
if (FAILED(hr))
{
dnd_debug(DL::DAMAGE, L"创建调试层失败!");
return;
}
debugController->EnableDebugLayer();
#endif
4.创建DXGI的Factory接口
IDXGIFactory是DXGI的基础接口,我们需要用com_ptr保存它的指针。可能你还看到其他人用ComPtr,这是偏旧一些的代码。
//.h
com_ptr<_IDXGIFactory> _factory;
//.cpp
hr = CreateDXGIFactory1(__uuidof(_IDXGIFactory), (void**)(&_factory));
if (FAILED(hr))
{
dnd_debug(DL::DAMAGE, L"F创建DXGI接口失败!");
return;
}
5.获取硬件适配器
IDXGIAdapter是显卡的抽象,但不一定是硬件的。通过GetHardwareAdapter函数返回硬件适配器,并attach赋予指针所有权给com_ptr<_IDXGIAdapter>。
//.h
com_ptr<_IDXGIAdapter> _adapter;
//.cpp
_IDXGIAdapter* adapter;
GetHardwareAdapter(_factory.get(), &adapter);
if (adapter == nullptr)
{
dnd_debug(DL::DAMAGE, L"获取硬件适配器失败!");
return;
}
_adapter.attach(adapter);
void GetHardwareAdapter(_IDXGIFactory* pFactory, _IDXGIAdapter** ppAdapter)
{
*ppAdapter = nullptr;
for (UINT adapterIndex = 0; ; ++adapterIndex)
{
_IDXGIAdapter* pAdapter = nullptr;
if (DXGI_ERROR_NOT_FOUND == pFactory->EnumAdapterByGpuPreference(
adapterIndex,
DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, _uuidof(_IDXGIAdapter), (void**)&pAdapter))
{
// No more adapters to enumerate.
break;
}
// Check to see if the adapter supports Direct3D 12, but don't create the
// actual device yet.
if (SUCCEEDED(D3D12CreateDevice(pAdapter, D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr)))
{
*ppAdapter = pAdapter;
return;
}
pAdapter->Release();
}
}
6.输出显卡信息(可选)
//输出显卡信息
DXGI_ADAPTER_DESC adapter_desc;
_adapter->GetDesc(&adapter_desc);
dnd_debug(DL::MSG, adapter_desc.Description);
dnd_debug(DL::MSG, L"显卡内存:" + to_wstring(int(adapter_desc.DedicatedVideoMemory / 1024 / 1024)));
dnd_debug(DL::MSG, L"独占内存:" + to_wstring(int(adapter_desc.DedicatedSystemMemory / 1024 / 1024)));
dnd_debug(DL::MSG, L"共享内存:" + to_wstring(int(adapter_desc.SharedSystemMemory / 1024 / 1024)));
7.创建Device
其中D3D_FEATURE_LEVEL_11_0是最低要求显卡支持的特性等级(feature level),注意在GetHardwareAdapter函数里,我们也是填的这个值。这是官方文档的方法,应该不能填再低于它的值。
hr = D3D12CreateDevice(
_adapter.get(),
D3D_FEATURE_LEVEL_11_0,
IID_PPV_ARGS(&_device));
if (FAILED(hr))
{
dnd_debug(DL::DAMAGE, L"创建Device失败!");
return;
}
8.创建命令队列(command queue)
命令队列用于执行命令列表(command list),简单情况只需要创建一个。
D3D12_COMMAND_QUEUE_DESC desc;
desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
desc.NodeMask = 0;
desc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL;
desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
hr = _device->CreateCommandQueue(&desc, IID_PPV_ARGS(&_commandQueue));
if (FAILED(hr))
{
dnd_debug(DL::DAMAGE, L"创建命令队列失败!");
return;
}
9.创建交换链(swap chain)
| SwapEffect | 交换模型 | DirectX12应该使用DXGI_SWAP_EFFECT_FLIP_DISCARD交换模型 |
| BufferCount | 缓冲数量 | 翻转丢弃模型,此值必须大于2 |
| BufferUsage | 缓冲标记 | 作为绘制表面(render target)应该填DXGI_USAGE_RENDER_TARGET_OUTPUT |
| OutputWindow | 窗口句柄 | 填入窗口句柄hwnd |
| Windowed | 窗口模式 | 这个值最好为TRUE,切换全屏是之后的事 |
| Flag | 标记 | 填入DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH让DXGI可以自行修改缓冲大小,由于不需要响应alt+enter操作,直接填0 |
| Format | 颜色格式 | 统一使用DXGI_FORMAT_R8G8B8A8_UNORM |
| Width | 缓冲宽度 | 比如800 |
| Height | 缓冲高度 | 比如600 |
BufferDesc其余的刷新率、扫描方式字段如下填默认值即可,因为全屏模式,还需要匹配这些信息。
而SamleDesc多重采样(MSAA)字段,Count填1,Quality填0即可。因为翻转丢弃模型不支持多重采样。
com_ptr<IDXGISwapChain> swapChain;
DXGI_SWAP_CHAIN_DESC descSwapChain;
descSwapChain.BufferCount = SWAP_CHAIN_BUFFER_COUNT;
descSwapChain.BufferDesc.Format = DXGI_FORMAT_TYPE;
descSwapChain.BufferDesc.Height = h;
descSwapChain.BufferDesc.RefreshRate.Denominator = 0;
descSwapChain.BufferDesc.RefreshRate.Numerator = 0;
descSwapChain.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
descSwapChain.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
descSwapChain.BufferDesc.Width = w;
descSwapChain.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
descSwapChain.Flags = 0;// DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
descSwapChain.OutputWindow = g_system.GetHwnd();
descSwapChain.SampleDesc.Count = 1;
descSwapChain.SampleDesc.Quality = 0;
descSwapChain.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
descSwapChain.Windowed = TRUE;
hr = _factory->CreateSwapChain(
_commandQueue.get(),
&descSwapChain,
swapChain.put()
);
if (FAILED(hr))
{
dnd_debug(DL::DAMAGE, L"创建交换链失败!");
return;
}
swapChain.as(_swapChain);
10.禁止alt+enter的功能
创建交换链关联窗口后调用才有效。
_factory->MakeWindowAssociation(g_system.GetHwnd(), DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER);
11.记录当前后背缓冲索引
UINT _frameIndex = _swapChain->GetCurrentBackBufferIndex();
12.创建绘制表面视图(rtv)堆描述符
绘制表面视图(render target view),缩写rtv。
堆描述符(heap descriptor),用于描述多个资源的抽象。所以我们创建了一个_rtvHeap来表示对rtv的引用,其中NumDescriptors字段和创建交换链时填入的缓冲数量一致。
D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {};
rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
rtvHeapDesc.NodeMask = 0;
rtvHeapDesc.NumDescriptors = SWAP_CHAIN_BUFFER_COUNT;//
rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
hr = _device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&_rtvHeap));
if (FAILED(hr))
{
dnd_debug(DL::DAMAGE, L"创建RTV堆描述符失败!");
return;
}

本文详细介绍了DirectX12中创建基本组件的步骤,包括头文件、类型名字、检测XMath支持、开启调试层、创建DXGI Factory接口、获取硬件适配器、输出显卡信息、创建Device、命令队列、交换链、禁止alt+enter功能、记录后背缓冲索引、创建rtv和dsv堆描述符、创建rtv、命令分配器、dsv、命令列表、同步资源、根签名、输入布局、编译shader、创建管道状态对象、采样器、索引缓冲、绘制流程。内容涵盖从初始化到绘图的整个过程,适合初学者和开发者参考。
最低0.47元/天 解锁文章
989

被折叠的 条评论
为什么被折叠?



