# 输入布局

# 顶点缓冲区

GPU 会从 缓冲区 中读取数据并进行处理,如之前提到的 回读堆、默认堆 等。 创建顶点缓冲区和应用流程
1. D3D12_RESOURCE_DESC 结构体描述缓冲区资源。
2. ID3D12Device::CreateCommittedResource 创建 ID3D12Resource 对象。
3. 绑定到顶点缓冲区视图 (可以通过 ID3D12Resource 获得堆中地址)
4. 将视图绑定到渲染流水线上的一个插槽中 (input slot, 通过 ID3D12GraphicsCmmandList::IASVertexBuffers, 每个插槽可以绑定多个连续的缓冲区)
5. 通过 ID3D12GraphicsCmmandList::DrawInstanced 方法绘制顶点 通常场景中静态几何体都会使用默认堆进行存储来优化性能。
但是 CPU 无法访问默认堆,所以 D3D 提供了一个 上传缓冲区 资源。CPU 阶段需要将数据传递到 上传缓冲区 中,上传缓冲区负责将数据从 CPU 传递到 GPU 中。 D3D 那本书中提供了一个创建资源的封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
Microsoft::WRL::ComPtr<ID3D12Resource> d3dUtil::CreateDefaultBuffer(
ID3D12Device* device,
ID3D12GraphicsCommandList* cmdList,
const void* initData,
UINT64 byteSize,
Microsoft::WRL::ComPtr<ID3D12Resource>& uploadBuffer)
{
ComPtr<ID3D12Resource> defaultBuffer;
CD3DX12_HEAP_PROPERTIES pro = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);
CD3DX12_RESOURCE_DESC cd = CD3DX12_RESOURCE_DESC::Buffer(byteSize);
// 创建实际的资源缓冲
ThrowIfFailed(device->CreateCommittedResource(
&pro,
D3D12_HEAP_FLAG_NONE,
&cd,
D3D12_RESOURCE_STATE_COMMON,
nullptr,
IID_PPV_ARGS(defaultBuffer.GetAddressOf())));

// 为了将CPU端内存中的数据复制到默认缓冲区,需要创建一个上传堆
//上传堆
CD3DX12_HEAP_PROPERTIES cp = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD);
CD3DX12_RESOURCE_DESC cdd = CD3DX12_RESOURCE_DESC::Buffer(byteSize);
ThrowIfFailed(device->CreateCommittedResource(
&cp,
D3D12_HEAP_FLAG_NONE,
&cdd,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(uploadBuffer.GetAddressOf())));


// 填充我们希望复制到默认缓冲区中的数据
D3D12_SUBRESOURCE_DATA subResourceData = {};
subResourceData.pData = initData;
subResourceData.RowPitch = byteSize;
subResourceData.SlicePitch = subResourceData.RowPitch;

// 将数据复制到默认缓冲区资源的流程.
// UpdateSubresources辅助函数会将数据从CPU复制到上传堆中,
// 接着调用ID3D12CommandList::CopySubresourceRegion,将数据从上传堆传递至缓冲区
CD3DX12_RESOURCE_BARRIER cb = CD3DX12_RESOURCE_BARRIER::Transition(defaultBuffer.Get(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_DEST);
cmdList->ResourceBarrier(1, &cb);
//实际上传函数UpdateSubresources
UpdateSubresources<1>(cmdList, defaultBuffer.Get(), uploadBuffer.Get(), 0, 0, 1, &subResourceData);
CD3DX12_RESOURCE_BARRIER cddd = CD3DX12_RESOURCE_BARRIER::Transition(defaultBuffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_GENERIC_READ);
cmdList->ResourceBarrier(1, &cddd);

// Note: uploadBuffer 在调用后必须一直存在,不能立即销毁,因为复制命令是异步的
// 当收到复制成功后的消息方可销毁

return defaultBuffer;
}

# 索引缓冲区

# 顶点着色器

d3d 中,使用的着色器语言是 HLSL , 其语法和 C++ 比较类似.

# 数据匹配原则

顶点着色器的数据是需要从 CPU 传递到 GPU 的,而顶点着色器所需要的参数与 CPU 阶段组合的对象虽然不需要有完全一致的布局, 但是 cpu 传递的数据必须有 GPU 所需要的那部分,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
struct Vertex{
XMFLOAT3 Pos;
XMFLOAT4 Color;
XMFLOAT3 Normal;
};

D3D12_INPUT_ELEMENT_DESC desc[] = {
{"POSITION",0,DXGI_FORMAT_R32G32B32_FLOAT,0,0,D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,0},
{"COLOR",0,DXGI_FORMAT_R32G32B32_FLOAT,0,12,D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,0},
{"NORMAL",0,DXGI_FORMAT_R32G32B32_FLOAT,0,28,D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,0}
}

对应的顶点着色器的输入:

1
2
3
4
struct VertexIn{
float3 PosL : POSITION;
float4 Color : COLOR;
}

可以看出,实际上顶点着色器的这个输入比 CPU结构体 少一个参数, 但是,顶点着色器的参数不能比CPU多一些布局

# 像素着色器

在像素着色器中,需要重点区分开的两个概念就是 像素片段像素 像素才是最终在屏幕上绘制的元素

# Early-Z 技术

可以使用 Early-Z 技术来减少计算的数量,Early-Z 技术是在像素着色器之前就进行剔除操作 (各种测试). 像素着色器内的输入实际上是经过重心坐标插值后的结果

# 常量缓冲区 - cBuffer

常量缓冲区是一种 GPU 资源,常量缓冲区的数据存储在上传堆中,且常量缓冲区每帧都会从 CPU 更新一次,常量缓冲区大小必须是 256B 的整倍数.

从 C++ 层创建常量缓冲区代码:

# 更新常量缓冲区

我们可以创建常量缓冲区,但如何更新常量缓冲区的值呢?可以用上传堆的 Map 方法来做到这一点. 使用双重指针让上传堆的数据指针直接指向 CPU 内存 常量缓冲区更新完成后,在对数据进行释放内存操作前,需要进行 UNMAP 操作 第一个参数都是 子资源索引

# 常量缓冲区与流水线

常量缓冲区也需要使用常量缓冲区描述符绑定到渲染流水线上 常量缓冲区的描述符是 D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV
(描述符存放在描述符堆中) 为了建立这样的描述符,同样需要建立一个描述变量 D3D12_DESCRIPTOR_HEAP_DESC , 然后设置 Type 字段为上述描述符即可 创建时就使用 device->CreateDescriptorHear(堆引用,IID_PPV_ARGS(描述符))

实际的常量缓冲区创建是在描述符创建完毕后创建的

通过根签名将流水线上实际使用的资源输入绑定到流水线上

DXD12 中说,实际上可以将着色器程序看作是一个函数,而根签名就是函数签名,通过函数签名来指定参数

以及实际的资源都存放在 GPU 堆中的 寄存器 中, 至于这个寄存器是真的寄存器还是虚拟的寄存器,暂时不太清楚

# 着色器编译

可以使用 fxc 工具离线编译着色器,也可以运行时编译 FXC 会在编译前整理着色器代码, 如去掉分支等

# 光栅器状态

如:是否开启背面剔除、是否以逆时针绕序为正方向

# 流水线状态对象

PSO(Pipeline State Object) 通过这个描述可以看出,实际上流水线状态对象上需要绑定 顶点着色器、片元着色器 D3D 实际上就是一个状态机,可以通过更改 PSO 来更改当前渲染流水线的渲染方式 (渲染状态)

# Chapter6 代码

说实话这一章信息量有点大,可能需要消化消化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
//***************************************************************************************
// BoxApp.cpp by Frank Luna (C) 2015 All Rights Reserved.
//
// Shows how to draw a box in Direct3D 12.
//
// Controls:
// Hold the left mouse button down and move the mouse to rotate.
// Hold the right mouse button down and move the mouse to zoom in and out.
//***************************************************************************************

#include "../Common/d3dApp.h"
#include "../Common/MathHelper.h"
#include "../Common/UploadBuffer.h"
#include<DirectXColors.h>

using Microsoft::WRL::ComPtr;
using namespace DirectX;
using namespace DirectX::PackedVector;

struct Vertex {
XMFLOAT3 Pos;
XMFLOAT4 Color;
};

struct ObjectConstants {//常量缓冲区
XMFLOAT4X4 WorldViewProj = MathHelper::Identity4x4();
};

class BoxApp : public D3DApp {
public:
BoxApp(HINSTANCE hInstance);
BoxApp(const BoxApp& rhs) = delete;//禁止拷贝构造
~BoxApp();

virtual bool Initialize() override;
private:
virtual void OnResize() override;
virtual void Update(const GameTimer& gt) override;
virtual void Draw(const GameTimer& gt) override;

virtual void OnMouseDown(WPARAM btnState, int x, int y) override;
virtual void OnMouseUp(WPARAM btnState, int x, int y) override;
virtual void OnMouseMove(WPARAM btnState, int x, int y) override;

void BuildDescriptorHeaps();
void BuildConstantBuffers();
void BuildRootSignature();
void BuildShadersAndInputLayout();
void BuildBoxGeometry();
void BuildPSO();

private:
ComPtr<ID3D12RootSignature> mRootSignature = nullptr;
ComPtr<ID3D12DescriptorHeap> mCbvHeap = nullptr;

//常量缓冲区(上传堆)
std::unique_ptr<UploadBuffer<ObjectConstants>> mObjectCB = nullptr;
std::unique_ptr<MeshGeometry> mBoxGeo = nullptr;

ComPtr<ID3DBlob> mvsByteCode = nullptr;
ComPtr<ID3DBlob> mpsByteCode = nullptr;

std::vector<D3D12_INPUT_ELEMENT_DESC> mInputLayout;

ComPtr<ID3D12PipelineState> mPSO = nullptr;

XMFLOAT4X4 mWorld = MathHelper::Identity4x4();
XMFLOAT4X4 mView = MathHelper::Identity4x4();
XMFLOAT4X4 mProj = MathHelper::Identity4x4();

float mTheta = 1.5f * XM_PI;
float mPhi = XM_PIDIV4;
float mRadius = 5.0f;

POINT mLastMousePos;
};

BoxApp::BoxApp(HINSTANCE hInstance) : D3DApp(hInstance) {

}

BoxApp::~BoxApp() {

}

bool BoxApp::Initialize() {
if (!D3DApp::Initialize()) {
return false;
}
//重置命令列表为执行初始化命令做好准备
ThrowIfFailed(mCommandList->Reset(mDirectCmdListAlloc.Get(), nullptr));

BuildDescriptorHeaps();
BuildConstantBuffers();
BuildRootSignature();
BuildShadersAndInputLayout();
BuildBoxGeometry();
BuildPSO();

ThrowIfFailed(mCommandList->Close());//切换到关闭态,停止记录
ID3D12CommandList* cmdLists[] = { mCommandList.Get() };
mCommandQueue->ExecuteCommandLists(_countof(cmdLists), cmdLists);

//等待初始化完成
FlushCommandQueue();
return true;
}

void BoxApp::OnResize() {
D3DApp::OnResize();
//若用户调整了屏幕尺寸,则更新纵横比并重新计算投影矩阵
XMMATRIX P = XMMatrixPerspectiveFovLH(0.25f * MathHelper::Pi, AspectRatio(), 1.0f, 1000.0f);
XMStoreFloat4x4(&mProj, P);
}

void BoxApp::Update(const GameTimer& gt) {
//将球坐标转换为笛卡尔坐标(相机坐标?)
float x = mRadius * sinf(mPhi) * cosf(mTheta);
float z = mRadius * sinf(mPhi) * sinf(mTheta);
float y = mRadius * cosf(mPhi);

//构建观察矩阵(Look-At)
XMVECTOR pos = XMVectorSet(x, y, z, 1.0f);
XMVECTOR target = XMVectorZero();
XMVECTOR up = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);

XMMATRIX view = XMMatrixLookAtLH(pos, target, up);
XMStoreFloat4x4(&mView, view);

XMMATRIX world = XMLoadFloat4x4(&mWorld);
XMMATRIX proj = XMLoadFloat4x4(&mProj);
XMMATRIX wvp = world * view * proj;

//将最新的wvp矩阵更新到常量缓冲区中
ObjectConstants objConstants;
XMStoreFloat4x4(&objConstants.WorldViewProj, XMMatrixTranspose(wvp));
mObjectCB->CopyData(0, objConstants);
}


void BoxApp::Draw(const GameTimer& gt) {
//复用记录命令所用的内存
//只有当GPU中的命令列表执行完毕后,才可以对其进行充值
ThrowIfFailed(mDirectCmdListAlloc->Reset());

//通过函数ExecuteCommandList将命令列表加入命令队列后,便可对它进行重置
//复用命令列表即复用其相应的内存
ThrowIfFailed(mCommandList->Reset(mDirectCmdListAlloc.Get(),mPSO.Get()));
mCommandList->RSSetViewports(1, &mScreenViewport);
mCommandList->RSSetScissorRects(1, &mScissorRect);

//按照资源的用途指示其状态的转变,此处将资源从呈现态转换为渲染目标态
//转换的是交换链的资源
mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(
CurrentBackBuffer(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET
));

//清除后缓冲区和深度缓冲区
mCommandList->ClearRenderTargetView(CurrentBackBufferView(), Colors::LightSteelBlue, 0, nullptr);
mCommandList->ClearDepthStencilView(DepthStencilView(),
D3D12_CLEAR_FLAG_DEPTH D3D12_CLEAR_FLAG_STENCIL,
1.0f, 0, 0, nullptr);

//指定将要渲染的目标缓冲区
mCommandList->OMSetRenderTargets(1, &CurrentBackBufferView(), true, &DepthStencilView());

ID3D12DescriptorHeap* descriptorHeaps[] = { mCbvHeap.Get() };
mCommandList->SetDescriptorHeaps(_countof(descriptorHeaps), descriptorHeaps);
mCommandList->SetGraphicsRootSignature(mRootSignature.Get());

mCommandList->IASetVertexBuffers(0, 1, &mBoxGeo->VertexBufferView());
mCommandList->IASetIndexBuffer(&mBoxGeo->IndexBufferView());
mCommandList->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

mCommandList->SetGraphicsRootDescriptorTable(
0, mCbvHeap->GetGPUDescriptorHandleForHeapStart()
);

//绘制命令
mCommandList->DrawIndexedInstanced(mBoxGeo->DrawArgs["box"].IndexCount, 1, 0, 0, 0);

//按照资源的用途指示其状态的转变,此处将资源从渲染目标状态转换为呈现态
mCommandList->ResourceBarrier(1,
&CD3DX12_RESOURCE_BARRIER::Transition(CurrentBackBuffer(),
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT));

ThrowIfFailed(mCommandList->Close());//切换到关闭态,停止记录
ID3D12CommandList* cmdLists[] = { mCommandList.Get() };
mCommandQueue->ExecuteCommandLists(_countof(cmdLists), cmdLists);

//交换前后台缓冲区
ThrowIfFailed(mSwapChain->Present(0, 0));
mCurrBackBuffer = (mCurrBackBuffer + 1) % SwapChainBufferCount;

//等待后续绘制的一系列命令执行完毕,
FlushCommandQueue();
}

void BoxApp::OnMouseDown(WPARAM btnState, int x, int y) {
mLastMousePos.x = x;
mLastMousePos.y = y;

SetCapture(mhMainWnd);
}

void BoxApp::OnMouseUp(WPARAM btnState, int x, int y) {
ReleaseCapture();
}

void BoxApp::OnMouseMove(WPARAM btnState, int x, int y) {
//按下才可以
if ((btnState & MK_LBUTTON) != 0) {
//根据鼠标的移动距离计算旋转角度,并令每个像素都按此角度的1/4旋转
float dx = XMConvertToRadians(0.25f * static_cast<float>(x - mLastMousePos.x));
float dy = XMConvertToRadians(0.25f * static_cast<float>(y - mLastMousePos.y));

//根据鼠标的输入来更新摄像机绕立方体旋转的角度
mTheta += dx;
mPhi += dy;

//限制角度mPhi的范围
mPhi = MathHelper::Clamp(mPhi, 0.1f, MathHelper::Pi - 0.01f);
}
else if ((btnState & MK_RBUTTON) != 0) {
//使场景中的每个像素按鼠标移动距离的0.005倍进行缩放
float dx = 0.005f * static_cast<float>(x - mLastMousePos.x);
float dy = 0.005f * static_cast<float>(y - mLastMousePos.y);

//根据鼠标输的输入更新摄像机的可视范围半径
mRadius += dx - dy;

mLastMousePos.x = x;
mLastMousePos.y = y;
}

}

/// <summary>
/// 构建描述符堆
/// </summary>
void BoxApp::BuildDescriptorHeaps()
{
D3D12_DESCRIPTOR_HEAP_DESC cbvHeapDesc;
cbvHeapDesc.NumDescriptors = 1;
cbvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
cbvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
cbvHeapDesc.NodeMask = 0;
ThrowIfFailed(md3dDevice->CreateDescriptorHeap(&cbvHeapDesc, IID_PPV_ARGS(&mCbvHeap)));
}

/// <summary>
/// 构建常量堆
/// Box实例中Shader只有一个
/// float4x4 gWorldViewProj
/// ObjectConstants里一样的内存布局
/// XMFLOAT4X4 WorldViewProj = MathHelper::Identity4x4();
/// </summary>
void BoxApp::BuildConstantBuffers() {
mObjectCB = std::make_unique<UploadBuffer<ObjectConstants>>(md3dDevice.Get(), 1, true);

UINT objCBByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(ObjectConstants));

D3D12_GPU_VIRTUAL_ADDRESS cbAddress = mObjectCB->Resource()->GetGPUVirtualAddress();
//偏移到常量缓冲区第i个物体所对应的常量数据
//这里取i=0
int boxCBufIndex = 0;
cbAddress += boxCBufIndex * objCBByteSize;

D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc;
cbvDesc.BufferLocation = cbAddress;
cbvDesc.SizeInBytes = d3dUtil::CalcConstantBufferByteSize(sizeof(ObjectConstants));

md3dDevice->CreateConstantBufferView(&cbvDesc,mCbvHeap->GetCPUDescriptorHandleForHeapStart());
}

void BoxApp::BuildRootSignature() {
//着色器一般需要资源输入(如常量缓冲区,纹理,采样等)
//根签名则定义了着色器程序所需的具体资源
//如果把着色器程序看作是一个函数,那么根签名就等价于函数签名

//跟参数可以是描述符,根据描述符或根常量
CD3DX12_ROOT_PARAMETER slotRootParameter[1];

//创建由单个CBV组成的描述附表
CD3DX12_DESCRIPTOR_RANGE cbvTable;
cbvTable.Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0);
slotRootParameter[0].InitAsDescriptorTable(1, &cbvTable);

//根签名就是一个跟参数构成的段
CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc(1, slotRootParameter, 0, nullptr,
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);

//用单个寄存器槽来创建一个根签名,该槽位指向一个仅包含单个常量缓冲区的描述符区域
ComPtr<ID3DBlob> serializedRootSig = nullptr;
ComPtr<ID3DBlob> errorBlob = nullptr;
HRESULT hr = D3D12SerializeRootSignature(&rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1,
serializedRootSig.GetAddressOf(),errorBlob.GetAddressOf());

if (errorBlob != nullptr) {
::OutputDebugStringA((char*)errorBlob->GetBufferPointer());
}
ThrowIfFailed(hr);

ThrowIfFailed(md3dDevice->CreateRootSignature(
0,
serializedRootSig->GetBufferPointer(),
serializedRootSig->GetBufferSize(),
IID_PPV_ARGS(&mRootSignature)
));
}

void BoxApp::BuildShadersAndInputLayout() {
HRESULT hr = S_OK;

mvsByteCode = d3dUtil::CompileShader(L"Shaders\\color.hlsl", nullptr,"VS","vs_5_0");
mpsByteCode = d3dUtil::CompileShader(L"Shaders\\color.hlsl", nullptr,"PS","ps_5_0");

mInputLayout = {
{"POSITION",0,DXGI_FORMAT_R32G32B32A32_FLOAT,0,0,
D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,0
},
{"COLOR",0,DXGI_FORMAT_R32G32B32A32_FLOAT,0,12,
D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,0
}
};
}

void BoxApp::BuildBoxGeometry() {
std::array<Vertex, 8> vertices =
{
Vertex({ XMFLOAT3(-1.0f, -1.0f, -1.0f), XMFLOAT4(Colors::White) }),
Vertex({ XMFLOAT3(-1.0f, +1.0f, -1.0f), XMFLOAT4(Colors::Black) }),
Vertex({ XMFLOAT3(+1.0f, +1.0f, -1.0f), XMFLOAT4(Colors::Red) }),
Vertex({ XMFLOAT3(+1.0f, -1.0f, -1.0f), XMFLOAT4(Colors::Green) }),
Vertex({ XMFLOAT3(-1.0f, -1.0f, +1.0f), XMFLOAT4(Colors::Blue) }),
Vertex({ XMFLOAT3(-1.0f, +1.0f, +1.0f), XMFLOAT4(Colors::Yellow) }),
Vertex({ XMFLOAT3(+1.0f, +1.0f, +1.0f), XMFLOAT4(Colors::Cyan) }),
Vertex({ XMFLOAT3(+1.0f, -1.0f, +1.0f), XMFLOAT4(Colors::Magenta) })
};

std::array<std::uint16_t, 36> indices =
{
// front face
0, 1, 2,
0, 2, 3,

// back face
4, 6, 5,
4, 7, 6,

// left face
4, 5, 1,
4, 1, 0,

// right face
3, 2, 6,
3, 6, 7,

// top face
1, 5, 6,
1, 6, 2,

// bottom face
4, 0, 3,
4, 3, 7
};

const UINT vbByteSize = (UINT)vertices.size() * sizeof(Vertex);
const UINT ibByteSize = (UINT)indices.size() * sizeof(std::uint16_t);

//构建一个Mesh的几何体
mBoxGeo = std::make_unique<MeshGeometry>();
mBoxGeo->Name = "boxGeo";

//填充顶点数据
ThrowIfFailed(D3DCreateBlob(vbByteSize, &mBoxGeo->VertexBufferCPU));
CopyMemory(mBoxGeo->VertexBufferCPU->GetBufferPointer(), vertices.data(), vbByteSize);

//填充索引数据
ThrowIfFailed(D3DCreateBlob(ibByteSize, &mBoxGeo->IndexBufferCPU));
CopyMemory(mBoxGeo->IndexBufferCPU->GetBufferPointer(), indices.data(), ibByteSize);

mBoxGeo->VertexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(),
mCommandList.Get(), vertices.data(), vbByteSize, mBoxGeo->VertexBufferUploader);

mBoxGeo->IndexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(),
mCommandList.Get(), indices.data(), ibByteSize, mBoxGeo->IndexBufferUploader);

mBoxGeo->VertexByteStride = sizeof(Vertex);
mBoxGeo->VertexBufferByteSize = vbByteSize;
mBoxGeo->IndexFormat = DXGI_FORMAT_R16_UINT;
mBoxGeo->IndexBufferByteSize = ibByteSize;

SubmeshGeometry submesh;
submesh.IndexCount = (UINT)indices.size();
submesh.StartIndexLocation = 0;
submesh.BaseVertexLocation = 0;

mBoxGeo->DrawArgs["box"] = submesh;
}

/// <summary>
/// 构建PSO - Pipeline State Object
/// 流水线状态机
/// </summary>
void BoxApp::BuildPSO()
{
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc;
ZeroMemory(&psoDesc, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
psoDesc.InputLayout = { mInputLayout.data(), (UINT)mInputLayout.size() };
psoDesc.pRootSignature = mRootSignature.Get();
psoDesc.VS =//顶点着色器
{
reinterpret_cast<BYTE*>(mvsByteCode->GetBufferPointer()),
mvsByteCode->GetBufferSize()
};
psoDesc.PS =//片元着色器
{
reinterpret_cast<BYTE*>(mpsByteCode->GetBufferPointer()),
mpsByteCode->GetBufferSize()
};
psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
psoDesc.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
psoDesc.SampleMask = UINT_MAX;
psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
psoDesc.NumRenderTargets = 1;
psoDesc.RTVFormats[0] = mBackBufferFormat;
psoDesc.SampleDesc.Count = m4xMsaaState ? 4 : 1;
psoDesc.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
psoDesc.DSVFormat = mDepthStencilFormat;
ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&mPSO)));
}


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, PSTR cmdLine, int showCmd) {
//为调试版本开启运行时内存检测,方便监督内存泄漏的情况
#if defined(DEBUG) defined(_DEBUG)
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF _CRTDBG_LEAK_CHECK_DF);
#endif

try {
BoxApp theApp(hInstance);
if (!theApp.Initialize())
return 0;
return theApp.Run();
}
catch (DxException e) {
MessageBox(nullptr, e.ToString().c_str(), L"HR Failed", MB_OK);
return 0;
}
}