-
Notifications
You must be signed in to change notification settings - Fork 0
/
ImageRenderer.cpp
401 lines (336 loc) · 19.5 KB
/
ImageRenderer.cpp
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
#include "stdafx.h"
#include "ImageRenderer.h"
#include "Camera.h"
#include "ObjLoader.h"
bool ImageRenderer::CreatePipelineState(ComPtr<ID3D12Device>& device, int width, int height) {
// Wird einmalig beim Start aufgerufen.
// init viewport and scissorRect with default values
m_viewport_ = CD3DX12_VIEWPORT(0.0f, 0.0f, static_cast<float>(width), static_cast<float>(height));
m_scissor_rect_ = CD3DX12_RECT(0, 0, static_cast<float>(width), static_cast<float>(height));
// create empty root signature
if (!this->CreateRootSignature(device)) {
Log::Error("Error create root signature");
return false;
}
ComPtr<ID3DBlob> vertexShader;
ComPtr<ID3DBlob> pixelShader;
// compile the shaders. Second param for function declared in vertexShader.hlsl
if (!this->CompileShaders(vertexShader, "rotate", pixelShader, "textured")) {
Log::Error("Error compile shaders");
return false;
}
// Define the vertex input layout.
D3D12_INPUT_ELEMENT_DESC inputElementDescs [] = {
{"POSITION", 0, DXGI_FORMAT_R32G32B32_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},
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 28, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}
};
// Describe and create the graphics pipeline state object (PSO).
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; // a structure to define a pso
psoDesc.InputLayout = {inputElementDescs, _countof(inputElementDescs)}; // The format of your vertex structure
psoDesc.pRootSignature = m_root_signature_.Get();
// A root signature is basically a parameter list of data that the shader functions expect. The shaders must be compatible with the Root Signature.
psoDesc.VS = CD3DX12_SHADER_BYTECODE(vertexShader.Get());
// A D3D12_SHADER_BYTECODE structure that describes the vertex shader.
psoDesc.PS = CD3DX12_SHADER_BYTECODE(pixelShader.Get());
// A D3D12_SHADER_BYTECODE structure that describes the pixel shader.
psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
// Rasterizer state such as cull mode, wireframe/solid rendering, antialiasing, etc. A D3D12_RASTERIZER_DESC structure.
psoDesc.RasterizerState.FrontCounterClockwise = true;
psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
// This describes the blend state that is used when the output merger writes a pixel fragment to the render target.
psoDesc.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
// Used for depth/stencil testing. Using default values of CD3DX12_DEPTH_STENCIL_DESC
psoDesc.DSVFormat = DXGI_FORMAT_D32_FLOAT; // A DXGI_FORMAT-typed value for the depth-stencil format.
psoDesc.SampleMask = UINT_MAX; // The sample mask for the blend state.
psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
// saying whether the Input Assembler should assemble the geometry as Points, Lines, Triangles, or Patches (used for tesselation).
// This is different from the adjacency and ordering that is set in the command list (triangle list, triangle strip, etc).
psoDesc.NumRenderTargets = 1; // it is possible to write to more than one render target at a time.
psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
// An array of DXGI_FORMAT-typed values for the render target formats.
psoDesc.SampleDesc.Count = 1; // The number of multisamples per pixel.
HRESULT hr = device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&m_pipeline_state_));
if (FAILED(hr)) {
Log::Error("Error create graphics pipeline state -ERROR:" + std::to_string(hr));
return false;
}
return true;
}
bool ImageRenderer::LoadResources(ComPtr<ID3D12Device>& device, ComPtr<ID3D12GraphicsCommandList>& commandList,
ComPtr<ID3D12CommandAllocator>& command_allocator, int width, int height) {
HRESULT hr;
// Wird einmalig beim Start aufgerufen.
hr = command_allocator->Reset();
if (FAILED(hr)) {
Log::Error("Error command_allocator->Reset() -ERROR:" + std::to_string(hr));
return false;
}
// Reset the command list
hr = commandList->Reset(command_allocator.Get(), m_pipeline_state_.Get());
if (FAILED(hr)) {
Log::Error("Error commandList->Reset -ERROR:" + std::to_string(hr));
return false;
}
Camera::SetAspectRatio(width, height);
Vertex* triangleVertices;
DWORD* iList;
int vCount;
BoundingVolume* boundingVolume;
ObjLoader::Load("models/plane.obj", triangleVertices, vCount, iList, i_list_size_, boundingVolume);
if (!this->CreateIndexBuffer(device, commandList, iList, sizeof(DWORD) * i_list_size_)) {
Log::Error("Error create index buffer -ERROR:");
return false;
}
if (!this->CreateVertexBuffer(device, commandList, triangleVertices, sizeof(Vertex) * vCount)) {
Log::Error("Error create vertex buffer -ERROR:");
return false;
}
if (!this->CreateDepthStencilBuffer(device, commandList, width, height)) {
Log::Error("Error create vdepth/stencil buffer -ERROR:");
return false;
}
if (!this->CreateConstantBuffer(device)) {
Log::Error("Error create constant buffer -ERROR:");
return false;
}
if (!LoadTexture(device, commandList)) {
Log::Error("Error loading texture -ERROR:");
return false;
}
hr = commandList->Close();
if (FAILED(hr)) {
Log::Error("Error close command list -ERROR:" + std::to_string(hr));
return false;
}
return true;
}
bool ImageRenderer::PopulateCommandList(ComPtr<ID3D12GraphicsCommandList>& command_list,
ComPtr<ID3D12CommandAllocator>& command_allocator,
CD3DX12_CPU_DESCRIPTOR_HANDLE& rtv_handle,
ComPtr<ID3D12Resource>& render_target,
int frame_index) {
// Wird mit jedem Frame aufgerufen.
// we can only reset an allocator once the gpu is done with it
// resetting an allocator frees the memory that the command list was stored in
HRESULT hr = command_allocator->Reset();
if (FAILED(hr)) {
Log::Error("Error command_allocator->Reset() -ERROR:" + std::to_string(hr));
return false;
}
// Reset the command list
hr = command_list->Reset(command_allocator.Get(), m_pipeline_state_.Get());
if (FAILED(hr)) {
Log::Error("Error command_list->Reset -ERROR:" + std::to_string(hr));
return false;
}
command_list->SetGraphicsRootSignature(m_root_signature_.Get()); // set the root signature
command_list->RSSetViewports(1, &m_viewport_); // set the viewports
command_list->RSSetScissorRects(1, &m_scissor_rect_); // set the scissor rects
// here we start recording commands into the command_list (which all the commands will be stored in the command_allocator)
// transition the "frame_index" render target from the present state to the render target state so the command list draws to it starting from here
command_list->ResourceBarrier(
1, &CD3DX12_RESOURCE_BARRIER::Transition(render_target.Get(), D3D12_RESOURCE_STATE_PRESENT,
D3D12_RESOURCE_STATE_RENDER_TARGET));
// set the render target for the output merger stage (the output of the pipeline)
// get a handle to the depth/stencil buffer
CD3DX12_CPU_DESCRIPTOR_HANDLE dsvHandle(m_depth_stencil_descriptor_heap_->GetCPUDescriptorHandleForHeapStart());
// set the render target for the output merger stage (the output of the pipeline)
command_list->OMSetRenderTargets(1, &rtv_handle, FALSE, &dsvHandle);
// Clear the render target by using the ClearRenderTargetView command
const float clearColor[] = WHITE;
command_list->ClearRenderTargetView(rtv_handle, clearColor, 0, nullptr);
// clear the depth/stencil buffer
command_list->ClearDepthStencilView(m_depth_stencil_descriptor_heap_->GetCPUDescriptorHandleForHeapStart(),
D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr);
// set constant buffer descriptor heap
ID3D12DescriptorHeap* descriptorHeaps[] = {m_main_descriptor_heap_[frame_index].Get()};
command_list->SetDescriptorHeaps(_countof(descriptorHeaps), descriptorHeaps);
// set the descriptor table to the descriptor heap (parameter 1, as constant buffer root descriptor is parameter index 0)
command_list->
SetGraphicsRootDescriptorTable(1, m_main_descriptor_heap_[frame_index]->GetGPUDescriptorHandleForHeapStart());
// draw triangle
command_list->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); // set the primitive topology
command_list->IASetVertexBuffers(0, 1, &m_vertex_buffer_view_); // set the vertex buffer (using the vertex buffer view)
command_list->IASetIndexBuffer(&m_index_buffer_view_);
command_list->SetGraphicsRootConstantBufferView(0, m_constant_buffer_upload_heap_[frame_index]->GetGPUVirtualAddress());
command_list->DrawIndexedInstanced(i_list_size_, 1, 0, 0, 0); // finally draw 3 vertices (draw the first triangle)
// transition the "frame_index" render target from the render target state to the present state. If the debug layer is enabled, you will receive a
// warning if present is called on the render target when it's not in the present state
command_list->ResourceBarrier(
1, &CD3DX12_RESOURCE_BARRIER::Transition(render_target.Get(), D3D12_RESOURCE_STATE_RENDER_TARGET,
D3D12_RESOURCE_STATE_PRESENT));
hr = command_list->Close();
if (FAILED(hr)) {
Log::Error("Error close command list -ERROR:" + std::to_string(hr));
return false;
}
return true;
}
void ImageRenderer::Update(int frame_index, double delta) {
// Update app logic, such as moving the camera or figuring out what objects are in view
// Update constant buffer
// create the wvp matrix and store in constant buffer
XMMATRIX viewMat = XMLoadFloat4x4(&Camera::GetViewMatrix()); // load view matrix
XMMATRIX projMat = XMLoadFloat4x4(&Camera::GetProjectionMatrix()); // load projection matrix
XMMATRIX transposed = XMMatrixTranspose(viewMat * projMat); // must transpose wvp matrix for the gpu
XMStoreFloat4x4(&m_constant_buffer_.wvpMat, transposed); // store transposed wvp matrix in constant buffer
// copy our ConstantBuffer instance to the mapped constant buffer resource
memcpy(p_constant_buffer_gpu_address_[frame_index], &m_constant_buffer_, sizeof(m_constant_buffer_));
}
void ImageRenderer::Release() {
m_texture_buffer_.Reset(); // the resource heap containing our texture
m_texture_buffer_upload_heap_.Reset();
delete p_data_;
DepthQuadRenderer::Release();
}
bool ImageRenderer::CreateRootSignature(ComPtr<ID3D12Device>& device) {
HRESULT hr;
// create a root descriptor, which explains where to find the data for this root parameter
D3D12_ROOT_DESCRIPTOR rootCBVDescriptor;
rootCBVDescriptor.RegisterSpace = 0;
rootCBVDescriptor.ShaderRegister = 0;
// create a descriptor range (descriptor table) and fill it out
// this is a range of descriptors inside a descriptor heap
D3D12_DESCRIPTOR_RANGE descriptorTableRanges[1]; // only one range right now
descriptorTableRanges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
// this is a range of shader resource views (descriptors)
descriptorTableRanges[0].NumDescriptors = 1; // we only have one texture right now, so the range is only 1
descriptorTableRanges[0].BaseShaderRegister = 0; // start index of the shader registers in the range
descriptorTableRanges[0].RegisterSpace = 0; // space 0. can usually be zero
descriptorTableRanges[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
// this appends the range to the end of the root signature descriptor tables
// create a descriptor table
D3D12_ROOT_DESCRIPTOR_TABLE descriptorTable;
descriptorTable.NumDescriptorRanges = _countof(descriptorTableRanges); // we only have one range
descriptorTable.pDescriptorRanges = &descriptorTableRanges[0]; // the pointer to the beginning of our ranges array
// create a root parameter for the root descriptor and fill it out
D3D12_ROOT_PARAMETER rootParameters[2]; // two root parameters
rootParameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; // this is a constant buffer view root descriptor
rootParameters[0].Descriptor = rootCBVDescriptor; // this is the root descriptor for this root parameter
rootParameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
// our pixel shader will be the only shader accessing this parameter for now
// fill out the parameter for our descriptor table. Remember it's a good idea to sort parameters by frequency of change. Our constant
// buffer will be changed multiple times per frame, while our descriptor table will not be changed at all (in this tutorial)
rootParameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; // this is a descriptor table
rootParameters[1].DescriptorTable = descriptorTable; // this is our descriptor table for this root parameter
rootParameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
// our pixel shader will be the only shader accessing this parameter for now
// create a static sampler
D3D12_STATIC_SAMPLER_DESC sampler = {};
sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_BORDER;
sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_BORDER;
sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_BORDER;
sampler.MipLODBias = 0;
sampler.MaxAnisotropy = 0;
sampler.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
sampler.MinLOD = 0.0f;
sampler.MaxLOD = D3D12_FLOAT32_MAX;
sampler.ShaderRegister = 0;
sampler.RegisterSpace = 0;
sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
CD3DX12_ROOT_SIGNATURE_DESC rootSignatureDesc;
rootSignatureDesc.Init(_countof(rootParameters), // we have 2 root parameters
rootParameters, // a pointer to the beginning of our root parameters array
1, // we have one static sampler
&sampler, // a pointer to our static sampler (array)
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
// we can deny shader stages here for better performance
D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS);
ComPtr<ID3DBlob> errorBuff; // a buffer holding the error data if any
ComPtr<ID3DBlob> signature;
hr = D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &errorBuff);
if (FAILED(hr)) {
Log::Error("Error serialize root signature -ERROR:" + std::to_string(hr));
return false;
}
hr = device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(),
IID_PPV_ARGS(&m_root_signature_));
if (FAILED(hr)) {
Log::Error("Error create root signature -ERROR:" + std::to_string(hr));
return false;
}
return true;
}
bool ImageRenderer::LoadTexture(ComPtr<ID3D12Device>& device, ComPtr<ID3D12GraphicsCommandList>& commandList) {
HRESULT hr;
// Load the image from file
D3D12_RESOURCE_DESC textureDesc;
int imageBytesPerRow;
int imageSize = TextureLoader::LoadImageDataFromFile(&p_data_, textureDesc, L"thm.png", imageBytesPerRow);
// make sure we have data
if (imageSize <= 0) {
Log::Error("Image has no size -ERROR:");
return false;
}
// create a default heap where the upload heap will copy its contents into (contents being the texture)
hr = device->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), // a default heap
D3D12_HEAP_FLAG_NONE, // no flags
&textureDesc, // the description of our texture
D3D12_RESOURCE_STATE_COPY_DEST,
// We will copy the texture from the upload heap to here, so we start it out in a copy dest state
nullptr, // used for render targets and depth/stencil buffers
IID_PPV_ARGS(&m_texture_buffer_));
if (FAILED(hr)) {
Log::Error("Error create default heap in LoadTexture -ERROR:" + std::to_string(hr));
return false;
}
m_texture_buffer_->SetName(L"Texture Buffer Resource Heap");
UINT64 textureUploadBufferSize;
// this function gets the size an upload buffer needs to be to upload a texture to the gpu.
// each row must be 256 byte aligned except for the last row, which can just be the size in bytes of the row
// eg. textureUploadBufferSize = ((((width * numBytesPerPixel) + 255) & ~255) * (height - 1)) + (width * numBytesPerPixel);
//textureUploadBufferSize = (((imageBytesPerRow + 255) & ~255) * (textureDesc.Height - 1)) + imageBytesPerRow;
device->GetCopyableFootprints(&textureDesc, 0, 1, 0, nullptr, nullptr, nullptr, &textureUploadBufferSize);
// now we create an upload heap to upload our texture to the GPU
hr = device->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), // upload heap
D3D12_HEAP_FLAG_NONE, // no flags
&CD3DX12_RESOURCE_DESC::Buffer(textureUploadBufferSize),
// resource description for a buffer (storing the image data in this heap just to copy to the default heap)
D3D12_RESOURCE_STATE_GENERIC_READ, // We will copy the contents from this heap to the default heap above
nullptr,
IID_PPV_ARGS(&m_texture_buffer_upload_heap_));
if (FAILED(hr)) {
Log::Error("Error create upload heap in LoadTexture -ERROR:" + std::to_string(hr));
return false;
}
m_texture_buffer_upload_heap_->SetName(L"Texture Buffer Upload Resource Heap");
// store texture buffer in upload heap
D3D12_SUBRESOURCE_DATA textureData = {};
textureData.pData = &p_data_[0]; // pointer to our image data
textureData.RowPitch = imageBytesPerRow; // size of all our triangle vertex data
textureData.SlicePitch = imageBytesPerRow * textureDesc.Height; // also the size of our triangle vertex data
// Now we copy the upload buffer contents to the default heap
UpdateSubresources(commandList.Get(), m_texture_buffer_.Get(), m_texture_buffer_upload_heap_.Get(), 0, 0, 1, &textureData);
// transition the texture default heap to a pixel shader resource (we will be sampling from this heap in the pixel shader to get the color of pixels)
commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_texture_buffer_.Get(),
D3D12_RESOURCE_STATE_COPY_DEST,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE));
for (int i = 0; i < DXGE_FRAME_COUNT; i++) {
// create the descriptor heap that will store our srv
D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
heapDesc.NumDescriptors = 1;
heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
hr = device->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&m_main_descriptor_heap_[i]));
if (FAILED(hr)) {
Log::Error("Error create descriptor heap in LoadTexture -ERROR:" + std::to_string(hr));
return false;
}
// now we create a shader resource view (descriptor that points to the texture and describes it)
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
srvDesc.Format = textureDesc.Format;
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MipLevels = 1;
device->CreateShaderResourceView(m_texture_buffer_.Get(), &srvDesc,
m_main_descriptor_heap_[i]->GetCPUDescriptorHandleForHeapStart());
}
return true;
}