【DirectX12】1.基本组件创建和绘图流程

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

目录

基本组件的创建和绘图流程

1.头文件

2.类型名字

3.检测XMath支持

4.开启调试层

4.创建DXGI的Factory接口

5.获取硬件适配器

6.输出显卡信息(可选)

7.创建Device

8.创建命令队列(command queue)

9.创建交换链(swap chain)

10.禁止alt+enter的功能

11.记录当前后背缓冲索引

12.创建绘制表面视图(rtv)堆描述符

 13.创建深度模板视图(dsv)堆描述符

14.获取堆描述符偏移大小

15.创建rtv

16.创建命令分配器(command allocator)

17.创建dsv

18.创建命令列表(command list)

19.创建用于同步的资源

20.创建根签名(root signature)

21.创建输入布局(input element)

22.编译shader

23.创建管道状态对象(pipeline state object)

光栅(rasterizer)状态

混合(blend)状态

深度模板(depth stencil)状态

最终创建PSO

24.创建采样器

25.创建索引缓冲(index buffer)

填充内存

创建默认堆(heap default)

 上载堆(heap upload)的创建

转换资源状态

 创建索引缓冲视图(ibv)

26.执行命令列表

27.绘制流水线

重置命令分配器和命令列表

rtv、stv清屏

绘制

转换rtv状态

关闭命令列表并执行

呈现并进行下一帧

28.绘图流程

29.代码仓库


基本组件的创建和绘图流程


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
BufferDesc
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;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值