DirectX12的初始化主要分为以下若干步骤:

  1. 创建device和gifactory
  2. 创建与GPU通信同步相关的objects,command和fence
  3. 创建swap chain
  4. 为render target view和depth stencil view创建descriptor heap
  5. 创建back buffer和与之绑定的render target view
  6. 创建depth stencil buffer和与之绑定的depth stencil view
  7. 填充viewport和屏幕剪裁区域结构
  8. 绘制前:设置buffer,设置view,设置viewport和屏幕剪裁区域
  9. 绘制后:提交绘制,设置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_VIEWPORTD3D12_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_PRESENTD3D12_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的初始化的更多相关文章

  1. 用DirectX12绘制一个Cube

    之前一篇文章讲了DirectX12的初始化流程,现在来看看在此基础上如何绘制一个Cube. 首先,我们要为这个Cube准备一个shader,来告诉GPU绘制的具体流程,DirectX中的shader使 ...

  2. DirectX12 3D 游戏开发与实战第一章内容

    DirectX12 3D 第一章内容 学习目标 1.学习向量在几何学和数学中的表示方法 2.了解向量的运算定义以及它在几何学中的应用 3.熟悉DirectXMath库中与向量有关的类和方法 1.1 向 ...

  3. Java初始化过程

    这篇文章主要讲解Java在创建对象的时候,初始化的顺序.主要从以下几个例子中讲解: 继承关系中初始化顺序 初始化块与构造器的顺序 已经加载过的类的初始化顺序 加载父类,会不会加载子类 创建子类对象会不 ...

  4. nginx源码分析之模块初始化

    在nginx启动过程中,模块的初始化是整个启动过程中的重要部分,而且了解了模块初始化的过程对应后面具体分析各个模块会有事半功倍的效果.在我看来,分析源码来了解模块的初始化是最直接不过的了,所以下面主要 ...

  5. nginx源码分析之网络初始化

    nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网 ...

  6. Java类变量和成员变量初始化过程

    一.类的初始化 对于类的初始化:类的初始化一般只初始化一次,类的初始化主要是初始化静态成员变量. 类的编译决定了类的初始化过程. 编译器生成的class文件主要对定义在源文件中的类进行了如下的更改: ...

  7. Git学习笔记一:新建本地仓库及初始化

    1.百度搜索Git下载安装,直接按默认选项安装即可. 例如:Git-2.7.2-32-bit_setup.1457942412.exe 2.配置Git信息,建立版本仓库 (Alt+PrintScerr ...

  8. Spring MVC初始化参数绑定

    初始化参数绑定与类型转换很类似,初始化绑定时,主要是参数类型 ---单日期 在处理器类中配置绑定方法  使用@InitBinder注解 在这里首先注册一个用户编辑器 参数一为目标类型   proper ...

  9. SpringMVC初始化参数绑定--日期格式

    一.初始化参数绑定[一种日期格式] 配置步骤: ①:在applicationcontext.xml中只需要配置一个包扫描器即可 <!-- 包扫描器 --> <context:comp ...

随机推荐

  1. ABBYY FineReader中的其他格式

    ABBYY FineReade是一款功能强大的PDF编辑转换器,在内置任务窗口,您可以将 PDF 或图片转换成常见的格式(*.pptx. *.odt. *.html.*.epub.*.fb2.*.rt ...

  2. 通过jquery 获取下拉列表中选中的值对应的value

    <div class="col-sm-9"> <select id="device-type" class="form-contro ...

  3. 2020年的UWP(4)——UWP和等待Request的Desktop Extension

    上一篇我们讨论了UWP和Desktop Extension交互中,Desktop Extension执行后立即退出的场景.下图是提到的四种场景分类: 执行后立即退出 等待request,处理完后退出 ...

  4. poi 1182

    食物链 || 带权并查集 0:同类 1:吃 2:被吃 #include <cstdio> using namespace std; const int maxn=5e4+3; int f[ ...

  5. 六. Vue CLI详解

    1. Vue CLI理解 1.1 什么是Vue CLI 如果你只是简单写几个Vue的Demo程序, 那么你不需要Vue CLI,如果你在开发大型项目那么你需要它, 并且必然需要使用Vue CLI. 使 ...

  6. 【VSA】One-shot video-based person re-identification with variance subsampling algorithm

    目录 解决了什么问题 主要贡献和创新点 基本框架 提出的方法 01 variance confidence方差置信度 02 Variance Subsampling Algorithm 方差二次采样算 ...

  7. Java进阶专题(十八) 系统缓存架构设计 (下)

    前言 上章节介绍了Redis相关知识,了解了Redis的高可用,高性能的原因.很多人认为提到缓存,就局限于Redis,其实缓存的应用不仅仅在于Redis的使用,比如还有Nginx缓存,缓存队列等等.这 ...

  8. 20200315_python3.6去除标点符号

    line = "python3.6下进行去!@#$%^&*()除标点测试,:!大家好,:!&>啥都不是!@#¥%--&*(-.||" # python ...

  9. android studio问题备注

    androidTestCompile 'com.android.support:support-annotations:25.3.1'configurations.all { resolutionSt ...

  10. 第4.4节 Python解析与推导:列表解析、字典解析、集合解析

    一.    引言 经过前几个章节的介绍,终于把与列表解析的前置内容介绍完了,本节老猿将列表解析.字典解析.集合解析进行统一的介绍. 前面章节老猿好几次说到了要介绍列表解析,但老猿认为涉及知识层面比较多 ...