DirectX12的初始化
DirectX12的初始化主要分为以下若干步骤:
- 创建device和gifactory
- 创建与GPU通信同步相关的objects,command和fence
- 创建swap chain
- 为render target view和depth stencil view创建descriptor heap
- 创建back buffer和与之绑定的render target view
- 创建depth stencil buffer和与之绑定的depth stencil view
- 填充viewport和屏幕剪裁区域结构
- 绘制前:设置buffer,设置view,设置viewport和屏幕剪裁区域
- 绘制后:提交绘制,设置buffer
创建device和gifactory
主要调用如下两个接口:
HRESULT D3D12CreateDevice(
IUnknown *pAdapter,
D3D_FEATURE_LEVEL MinimumFeatureLevel,
REFIID riid,
void **ppDevice
);
HRESULT CreateDXGIFactory1(
REFIID riid,
void **ppFactory
);
这里,我们可以借用微软提供的ComPtr来管理要创建的相关对象:
ComPtr提供了一些常用的接口,重载了一些常用的操作符:
Get():获取原始的指针GetAddressOf():获取原始指针的地址operator&:释放原始指针指向的对象,然而再返回原始指针的地址
ComPtr<ID3D12Device> mDevice = nullptr;
ComPtr<IDXGISwapChain> mSwapChain = nullptr;
同时,为了方便填充这些创建接口,微软提供了一个宏定义IID_PPV_ARGS:
ThrowIfFailed(D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&mDevice)));
ThrowIfFailed(CreateDXGIFactory1(IID_PPV_ARGS(&mDxGiFactory)));
创建与GPU通信同步相关的objects,command和fence
command是用来从CPU提交相关指令给GPU执行的。这里需要三个objects:queue,allocator,list。queue是用来最终提交给CPU的数据结构,allocator负责为list提供空间管理,list提供了若干CPU提交相关指令的接口。
ThrowIfFailed(mDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&mCommandQueue)));
ThrowIfFailed(mDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&mCommandAlloc)));
ThrowIfFailed(mDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, mCommandAlloc.Get(), nullptr, IID_PPV_ARGS(&mCommandList)));
// must close otherwise call reset will raise a error
mCommandList->Close();
创建完之后记得调用Close()方法,否则后面调用Reset()时会因为状态未关闭而出错。
fence是用来管理CPU和GPU同步的对象,防止对同一资源进行冲突访问,创建接口如下:
HRESULT CreateFence(
UINT64 InitialValue,
D3D12_FENCE_FLAGS Flags,
REFIID riid,
void **ppFence
);
创建swap chain
HRESULT CreateSwapChain(
IUnknown *pDevice,
DXGI_SWAP_CHAIN_DESC *pDesc,
IDXGISwapChain **ppSwapChain
);
值得一提的是,DirectX12不支持用该API创建MSAA swap chain:
Direct3D 12 don't support creating MSAA swap chains--attempts to create a swap chain with SampleDesc.Count > 1 will fail. Instead, you create your own MSAA render target and explicitly resolve to the DXGI back-buffer for presentation as shown here.
为render target view和depth stencil view创建descriptor heap
heap就是用来存放管理view的结构,相关接口如下:
UINT GetDescriptorHandleIncrementSize(
D3D12_DESCRIPTOR_HEAP_TYPE DescriptorHeapType
);
HRESULT CreateDescriptorHeap(
const D3D12_DESCRIPTOR_HEAP_DESC *pDescriptorHeapDesc,
REFIID riid,
void **ppvHeap
);
通过GetDescriptorHandleIncrementSize可以知道heap上每个handle的大小,方便在取一个heap上多个view进行偏移。
创建back buffer和与之绑定的render target view
ThrowIfFailed(mSwapChain->ResizeBuffers(mBackBufferCount, windowWidth, windowHeight, mBackBufferFormat,
DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH));
CD3DX12_CPU_DESCRIPTOR_HANDLE handle = CD3DX12_CPU_DESCRIPTOR_HANDLE(
mRtvHeap->GetCPUDescriptorHandleForHeapStart());
for (int i = 0; i < mBackBufferCount; i++)
{
ThrowIfFailed(mSwapChain->GetBuffer(i, IID_PPV_ARGS(&mBackBuffer[i])));
mDevice->CreateRenderTargetView(mBackBuffer[i].Get(), nullptr, handle);
handle.Offset(1, mRtvHeapIncSize);
}
CD3DX12_CPU_DESCRIPTOR_HANDLE也是微软提供的便利数据结构,包含一些简单的接口。这里用了Offset方法来取得正确的heap位置。
创建depth stencil buffer和与之绑定的depth stencil view
ThrowIfFailed(mDevice->CreateCommittedResource(&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_COMMON, &clearValue, IID_PPV_ARGS(&mDepthStencilBuffer)));
handle = CD3DX12_CPU_DESCRIPTOR_HANDLE(mDsvHeap->GetCPUDescriptorHandleForHeapStart());
mDevice->CreateDepthStencilView(mDepthStencilBuffer.Get(), &viewDesc, handle);
与back buffer的过程类似,只不过depth stencil buffer只有一个,而back buffer可能有多个。CD3DX12_HEAP_PROPERTIES同样也是便利数据结构。
注意到buffer创建完后处于D3D12_RESOURCE_STATE_COMMON状态,我们需要将其转为D3D12_RESOURCE_STATE_DEPTH_WRITE状态,使用CD3DX12_RESOURCE_BARRIER::Transition进行便捷操作:
mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(mDepthStencilBuffer.Get(),
D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_DEPTH_WRITE));
ThrowIfFailed(mCommandList->Close());
ID3D12CommandList *cmdList[] = { mCommandList.Get() };
mCommandQueue->ExecuteCommandLists(_countof(cmdList), cmdList);
FlushCommandQueue();
void DirectX12::FlushCommandQueue()
{
mCurrentFence++;
ThrowIfFailed(mCommandQueue->Signal(mFence.Get(), mCurrentFence));
if (mFence->GetCompletedValue() < mCurrentFence)
{
HANDLE handle = CreateEventEx(nullptr, false, false, EVENT_ALL_ACCESS);
ThrowIfFailed(mFence->SetEventOnCompletion(mCurrentFence, handle));
WaitForSingleObject(handle, INFINITE);
CloseHandle(handle);
}
}
资源状态转换需要向GPU提交指令,因此涉及到同步问题,即CPU需要等待GPU完成指令之后才能继续执行其他操作。这里用了FlushCommandQueue来完成同步。这个方法的实现一目了然:
首先将当前fence计数值增加,signal提交到GPU,然后一直等待,直到获取到与计数值相同的值为止。
填充viewport和屏幕剪裁区域结构
这一步骤只需填充D3D12_VIEWPORT和D3D12_RECT结构即可。
mViewport.TopLeftX = 0;
mViewport.TopLeftY = 0;
mViewport.Width = windowWidth;
mViewport.Height = windowHeight;
mViewport.MinDepth = 0.0f;
mViewport.MaxDepth = 1.0f;
mScissorRect.left = 0;
mScissorRect.right = windowWidth;
mScissorRect.top = 0;
mScissorRect.bottom = windowHeight;
绘制前:设置buffer,设置view,设置viewport和屏幕剪裁区域
mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(mBackBuffer[mCurBackBuffer].Get(),
D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET));
mCommandList->RSSetScissorRects(1, &mScissorRect);
mCommandList->RSSetViewports(1, &mViewport);
CD3DX12_CPU_DESCRIPTOR_HANDLE dsv = CD3DX12_CPU_DESCRIPTOR_HANDLE(
mDsvHeap->GetCPUDescriptorHandleForHeapStart());
mCommandList->ClearDepthStencilView(dsv, D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0,
nullptr);
CD3DX12_CPU_DESCRIPTOR_HANDLE rtv = CD3DX12_CPU_DESCRIPTOR_HANDLE(
mRtvHeap->GetCPUDescriptorHandleForHeapStart(), mCurBackBuffer, mRtvHeapIncSize);
mCommandList->ClearRenderTargetView(rtv, LightSteelBlue, 0, nullptr);
mCommandList->OMSetRenderTargets(1, &rtv, true, &dsv);
这里将backBuffer的状态从D3D12_RESOURCE_STATE_PRESENT转移到了D3D12_RESOURCE_STATE_RENDER_TARGET,这样后面的设置和绘制工作才会有效,否则会出错。实际上,状态定义里,D3D12_RESOURCE_STATE_PRESENT和D3D12_RESOURCE_STATE_COMMON是等价的,都为0,这里只是换了一种说法。
绘制后:提交绘制,设置buffer
mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(mBackBuffer[mCurBackBuffer].Get(),
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT));
ThrowIfFailed(mCommandList->Close());
ID3D12CommandList *cmdList[] = { mCommandList.Get() };
mCommandQueue->ExecuteCommandLists(_countof(cmdList), cmdList);
ThrowIfFailed(mSwapChain->Present(0, 0));
mCurBackBuffer = (mCurBackBuffer + 1) % mBackBufferCount;
在调用swapChain的Present方法之前,必须将backbuffer的状态设置为D3D12_RESOURCE_STATE_PRESENT。如果使用了多个backbuffer,别忘记切换到下一个backbuffer。
如果你觉得我的文章有帮助,欢迎关注我的微信公众号(大龄社畜的游戏开发之路)-

DirectX12的初始化的更多相关文章
- 用DirectX12绘制一个Cube
之前一篇文章讲了DirectX12的初始化流程,现在来看看在此基础上如何绘制一个Cube. 首先,我们要为这个Cube准备一个shader,来告诉GPU绘制的具体流程,DirectX中的shader使 ...
- DirectX12 3D 游戏开发与实战第一章内容
DirectX12 3D 第一章内容 学习目标 1.学习向量在几何学和数学中的表示方法 2.了解向量的运算定义以及它在几何学中的应用 3.熟悉DirectXMath库中与向量有关的类和方法 1.1 向 ...
- Java初始化过程
这篇文章主要讲解Java在创建对象的时候,初始化的顺序.主要从以下几个例子中讲解: 继承关系中初始化顺序 初始化块与构造器的顺序 已经加载过的类的初始化顺序 加载父类,会不会加载子类 创建子类对象会不 ...
- nginx源码分析之模块初始化
在nginx启动过程中,模块的初始化是整个启动过程中的重要部分,而且了解了模块初始化的过程对应后面具体分析各个模块会有事半功倍的效果.在我看来,分析源码来了解模块的初始化是最直接不过的了,所以下面主要 ...
- nginx源码分析之网络初始化
nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网 ...
- Java类变量和成员变量初始化过程
一.类的初始化 对于类的初始化:类的初始化一般只初始化一次,类的初始化主要是初始化静态成员变量. 类的编译决定了类的初始化过程. 编译器生成的class文件主要对定义在源文件中的类进行了如下的更改: ...
- Git学习笔记一:新建本地仓库及初始化
1.百度搜索Git下载安装,直接按默认选项安装即可. 例如:Git-2.7.2-32-bit_setup.1457942412.exe 2.配置Git信息,建立版本仓库 (Alt+PrintScerr ...
- Spring MVC初始化参数绑定
初始化参数绑定与类型转换很类似,初始化绑定时,主要是参数类型 ---单日期 在处理器类中配置绑定方法 使用@InitBinder注解 在这里首先注册一个用户编辑器 参数一为目标类型 proper ...
- SpringMVC初始化参数绑定--日期格式
一.初始化参数绑定[一种日期格式] 配置步骤: ①:在applicationcontext.xml中只需要配置一个包扫描器即可 <!-- 包扫描器 --> <context:comp ...
随机推荐
- FL Studio在线面板的作用
FL Studio的在线面板主要是用来显示其自身与Image-Line公司相关的新闻.我们点击它后会看到一个新闻列表菜单,其中每一个项目都包含日期.标题.简介以及URL.详细的内容我们将在下文介绍,一 ...
- leetcode133. 克隆图
给定无向连通图中一个节点的引用,返回该图的深拷贝(克隆).图中的每个节点都包含它的值 val(Int) 和其邻居的列表(list[Node]).示例: 输入:{"$id":&quo ...
- Redis 基础数据结构之一:string(字符串)
Redis 有 5 种基础数据结构,分别为:string (字符串).list (列表).set (集合).hash (哈希) 和 zset (有序集合),Redis存储数据的结构是键值对形式的. 首 ...
- 如何解析 redis 的 rdb 文件
目录 安装工具 解析 redis 的 rdb 文件 命令行工具使用,先看 --help 生成内存报告 使用参数过滤想要的数据 比较两个 rdb 文件 查看一个 key 的内存使用情况 常见问题 FAQ ...
- 使用zabbix监控Jenkins
一.监控架构图 二.实现思路 在 Jenkins 上安装 Metrics 插件,使 Jenkins 暴露 metrics api: 编写python代码从api抓取数据,并将数据解析为zabbix可以 ...
- 记一次Ddos遭遇
万年不用的vps最近借朋友用了几天,今天突然跟我说连不上了 上服务器先暴力重启一波 还是不行,netstat一看 端口的连接状态是这个样子: 估计连接被打满了,遂换了个端口 重启之 问题解决
- wsgi_uwsgi_nginx
1.一些重要概念 https://www.cnblogs.com/xiaonq/p/8932266.html 1.1 web容器 什么是web容器 1.web容器是帮助我们部署java.php.pyt ...
- 删除list列表中的某一个元素的多种方法
当我们在处理业务的时候,很多情况下数据都要进行一层层的过滤,最近需要给一个列表中去除不符合条件的元素, 本来觉着挺简单的,Google了下发现很多方法都是旧方法,根本不符合我的需求. 于是参考着网上的 ...
- 19_B门长时曝光APP
知识很基础-- 前几天买了个单反,特别想拍B门长时间曝光的效果.后来想想不如自己写个APP,实现屏幕背景的随机颜色以及全屏显示文字. 先上图: 这两张图片的左侧都很亮,这是因为APP里面忘记把&quo ...
- 「刷题笔记」哈希,kmp,trie
Bovine Genomics 暴力 str hash+dp 设\(dp[i][j]\)为前\(i\)组匹配到第\(j\)位的方案数,则转移方程 \[dp[i][j+l]+=dp[i-1][j] \] ...