1.创建设备
设备代表显示适配器(如显卡)。设备即可以检测系统对功能的支持情况,又能创建其他接口对象(如资源,视图,命令列表)。
//创建设备
HRESULT hardwareResult = D3D12CreateDevice(
nullptr, // 创建设备时用的显示适配器,nullptr表示使用主显示适配器
D3D_FEATURE_LEVEL_11_0, // 应用程序需要硬件支持的最低功能级别
IID_PPV_ARGS(&md3dDevice)); // 返回创建的D3D设备
2.创建围栏并获取描述符的大小
围栏用于GPU/CPU的同步。
md3dDevice->CreateFence(
0, //标识围栏点的整数,初始为0,标记新的围栏点时就将它加1
D3D12_FENCE_FLAG_NONE,
IID_PPV_ARGS(&mFence)
);
使用描述符时,需要了解它们的大小。
//RTV(渲染目标视图)的大小
mRtvDescriptorSize = md3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
//DSV(深度/模板视图)的大小
mDsvDescriptorSize = md3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
//CBV_SRV_UAV(常量缓冲视图,着色器资源视图,无序访问视图)的大小
mCbvSrvUavDescriptorSize = md3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
3.检测对4X MSAA 质量级别的支持
检测对多重采样 MSAA(MultiSample Anti-Aliasing)的支持情况。
D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS msQualityLevels;
msQualityLevels.Format = mBackBufferFormat; //纹理格式
msQualityLevels.SampleCount = 4; //采样数量
msQualityLevels.Flags = D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG_NONE; //多重采样支持的标志
msQualityLevels.NumQualityLevels = 0; //图像质量级别
ThrowIfFailed(md3dDevice->CheckFeatureSupport(
D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, //检测对多重采样
&msQualityLevels, //对多重采样功能的支持信息。兼有输入输出属性,输出信息为图像质量级别
sizeof(msQualityLevels))); //数据结构的大小
m4xMsaaQuality = msQualityLevels.NumQualityLevels;
4.创建命令列表和命令队列
//队列描述
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
//创建队列
ThrowIfFailed(md3dDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&mCommandQueue)));
//创建命令分配器(命令列表里的命令,实际上是存储在与之关联的命令分配器里)
ThrowIfFailed(md3dDevice->CreateCommandAllocator(
D3D12_COMMAND_LIST_TYPE_DIRECT, //命令列表的类型,存储的是GPU可以直接执行的命令
IID_PPV_ARGS(mDirectCmdListAlloc.GetAddressOf()))); //指向命令分配器的指针
//创建命令列表
ThrowIfFailed(md3dDevice->CreateCommandList(
0, //0表示系统仅有一个GPU
D3D12_COMMAND_LIST_TYPE_DIRECT, //命令列表的类型,存储的是GPU可以直接执行的命令
mDirectCmdListAlloc.Get(), // 相关联命令分配器
nullptr, // 初始化流水线状态对象
IID_PPV_ARGS(mCommandList.GetAddressOf()))); //指向命令列表的指针
//将命令列表置于关闭状态。因为第一次引用命令列表时,要对它重置,而在调用重置方法之前又需要先将其关闭。
mCommandList->Close();
5.描述并创建交换链
//交换链描述
DXGI_SWAP_CHAIN_DESC sd; //BufferDesc描述待创建后台缓冲区的属性
sd.BufferDesc.Width = mClientWidth; //缓冲区分辨率宽度
sd.BufferDesc.Height = mClientHeight; //缓冲区分辨率高度
sd.BufferDesc.RefreshRate.Numerator = 60; //刷新频率
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.BufferDesc.Format = mBackBufferFormat; //缓冲区的显示格式
sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; //逐行扫描vs隔行扫描
sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; //图像如何相对于屏幕进行拉伸
sd.SampleDesc.Count = m4xMsaaState ? 4 : 1; //多重采样每个像素的采样次数
sd.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0; //多重采样的质量级别
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; //将数据渲染至后台缓冲区(即用它作为渲染目标)
sd.BufferCount = SwapChainBufferCount; //交换链中缓冲区数量
sd.OutputWindow = mhMainWnd; //渲染窗口的句柄
sd.Windowed = true; //true表示程序在窗口模式下运行,false表示采用全屏模式
sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; //切换到全屏模式时,他将选择最适于当前应用程序窗口尺寸的显示模式。
//创建交换链
ThrowIfFailed(mdxgiFactory->CreateSwapChain(
mCommandQueue.Get(), //指向命令队列的指针 (交换链需要通过队列对其进行刷新)
&sd, //指向描述符的指针
mSwapChain.GetAddressOf())); //返回所创建的交换链
6.创建描述符堆
描述符堆用来存储程序中用到的描述符。此处创建应用程序所需的RTV(渲染目标视图)和DSV(深度/模板视图)。
D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc;
rtvHeapDesc.NumDescriptors = SwapChainBufferCount;
rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
rtvHeapDesc.NodeMask = 0;
//创建描述符堆,用来存储程序中用到的描述符/视图(渲染目标视图)
ThrowIfFailed(md3dDevice->CreateDescriptorHeap(
&rtvHeapDesc, IID_PPV_ARGS(mRtvHeap.GetAddressOf())));
D3D12_DESCRIPTOR_HEAP_DESC dsvHeapDesc;
dsvHeapDesc.NumDescriptors = 1;
dsvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
dsvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
dsvHeapDesc.NodeMask = 0;
//创建描述符堆,用来存储程序中用到的描述符/视图(深度/模板视图)
ThrowIfFailed(md3dDevice->CreateDescriptorHeap(
&dsvHeapDesc, IID_PPV_ARGS(mDsvHeap.GetAddressOf())));
7.创建渲染目标视图
资源不能和渲染流水线直接绑定,需要先为资源创建视图(描述符),再将其绑定到流水线阶段。
//记录当前后台缓冲区的索引
mCurrBackBuffer = 0;
//获取描述符堆中,第一个描述符的的句柄
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHeapHandle(mRtvHeap->GetCPUDescriptorHandleForHeapStart());
for (UINT i = 0; i < SwapChainBufferCount; i++)
{
//获取交换链中的第i个缓冲区
ThrowIfFailed(mSwapChain->GetBuffer(i,IID_PPV_ARGS(&mSwapChainBuffer[i])));
//为此缓冲区创建一个RTV(渲染目标视图)
md3dDevice->CreateRenderTargetView(mSwapChainBuffer[i].Get(), //用作渲染目标的资源,后台缓冲区
nullptr, //资源中元素的数据格式,nullptr表示使用该资源创建时的格式
rtvHeapHandle); //RTV(渲染目标视图)的描述符句柄
//偏移到描述符堆中的下一个缓冲区
rtvHeapHandle.Offset(1, mRtvDescriptorSize);
}
8.创建深度/模板缓冲区及其视图
深度缓冲区是一种2D纹理,存储着深度信息(模板有模板信息)。
//创建深度/模板缓冲区及其视图
D3D12_RESOURCE_DESC depthStencilDesc;
depthStencilDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; //资源的维度 2D纹理
depthStencilDesc.Alignment = 0;
depthStencilDesc.Width = mClientWidth; //纹理宽度
depthStencilDesc.Height = mClientHeight; //纹理高度
depthStencilDesc.DepthOrArraySize = 1; //以纹素为单位表示纹理深度或者纹理数组的大小
depthStencilDesc.MipLevels = 1; //mipmap层级的数量
depthStencilDesc.Format = DXGI_FORMAT_R24G8_TYPELESS; //纹素的格式
depthStencilDesc.SampleDesc.Count = m4xMsaaState ? 4 : 1; //多重采样对每个像素的采样次数
depthStencilDesc.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0; //多重采样的质量级别
depthStencilDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; //纹理布局
depthStencilDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; //与资源相关的标记
D3D12_CLEAR_VALUE optClear;
optClear.Format = mDepthStencilFormat;
optClear.DepthStencil.Depth = 1.0f;
optClear.DepthStencil.Stencil = 0;
//根据属性创建一个资源和一个堆,并把资源提交到这个堆中
ThrowIfFailed(md3dDevice->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), //默认堆
D3D12_HEAP_FLAG_NONE, //与堆相关的标记
&depthStencilDesc, //资源描述
D3D12_RESOURCE_STATE_COMMON, //资源的使用状态
&optClear, //用于清除资源的优化值
IID_PPV_ARGS(mDepthStencilBuffer.GetAddressOf()))); //返回一个指向ID3D12Resource的指针,即新建的资源
//在使用深度/模板缓冲区之前,一定要创建相关的深度/模板视图,并将它绑定到渲染流水线上
D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc; //此结构体描述资源中元素的数据类型(格式)
dsvDesc.Flags = D3D12_DSV_FLAG_NONE;
dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
dsvDesc.Format = mDepthStencilFormat;
dsvDesc.Texture2D.MipSlice = 0;
md3dDevice->CreateDepthStencilView(mDepthStencilBuffer.Get(), &dsvDesc, DepthStencilView());
//将资源从初始状态转换为深度缓冲区
mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(mDepthStencilBuffer.Get(),
D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_DEPTH_WRITE));
9.设置视口
//定义了视口矩形相对于后台缓冲区的绘制范围
D3D12_VIEWPORT mScreenViewport;
mScreenViewport.TopLeftX = 0;
mScreenViewport.TopLeftY = 0;
mScreenViewport.Width = static_cast<float>(mClientWidth);
mScreenViewport.Height = static_cast<float>(mClientHeight);
mScreenViewport.MinDepth = 0.0f;
mScreenViewport.MaxDepth = 1.0f;
mCommandList->RSSetViewports(1, &mScreenViewport);
10.设置裁剪矩形
mScissorRect = { 0, 0, mClientWidth, mClientHeight };
mCommandList->RSSetScissorRects(1, &mScissorRect);