-
Notifications
You must be signed in to change notification settings - Fork 0
/
TriangleRenderer.cpp
297 lines (249 loc) · 13.8 KB
/
TriangleRenderer.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
#include "stdafx.h"
#include "TriangleRenderer.h"
/*
Resource Heaps are like descriptor heaps, but instead of storing descriptors,
they store the actual data for the resources. They are a chunk of allocated memory,
either on the GPU or on the CPU, depending on the type of heap.
Upload heaps -> used to upload data to the GPU. The GPU has read access to this memory, while the CPU has write access
vertex buffer is stored here and gets copied to the default heap
Default heaps -> Chunks of memory on the GPU. The CPU has no access to this memory, which makes accessing this memory in shaders very fast
here are the resources stored that the shaders will use
Input Layout (D3D12_INPUT_ELEMENT_DESC) -> describes how the input assembler should read the vertices in the vertex buffer
*/
bool TriangleRenderer::CreatePipelineState(ComPtr<ID3D12Device>& device, int width, int height) {
// Wird einmalig beim Start aufgerufen. Implementierung gemäß Vorlesung
// 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 createroot signature");
return false;
}
ComPtr<ID3DBlob> vertexShader;
ComPtr<ID3DBlob> pixelShader;
if (!this->CompileShaders(vertexShader, "main", pixelShader, "main")) {
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.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.DepthEnable = FALSE; // Used for depth/stencil testing
psoDesc.DepthStencilState.StencilEnable = FALSE; // Used for depth/stencil testing
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 TriangleRenderer::LoadResources(ComPtr<ID3D12Device>& m_device, ComPtr<ID3D12GraphicsCommandList>& command_list,
ComPtr<ID3D12CommandAllocator>& command_allocator, int width, int height) {
HRESULT hr;
// Reset the command allocator
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;
}
// create triangle vertices
Vertex triangleVertices[] =
{
{{-0.25f, 0.25f, 0.0f}, THM_GREEN},
{{0.25f, -0.25f, 0.0f}, THM_GREEN},
{{-0.25f, -0.25f, 0.0f}, THM_GREEN},
{{0.25f, -0.25f, 0.0f}, THM_RED},
{{-0.25f, 0.25f, 0.0f}, THM_RED},
{{0.25f, 0.25f, 0.0f}, THM_RED},
};
if (!this->CreateVertexBuffer(m_device, command_list, triangleVertices, sizeof(triangleVertices))) {
Log::Error("Error create vertex buffer -ERROR:");
return false;
}
hr = command_list->Close();
if (FAILED(hr)) {
Log::Error("Error close command list -ERROR:" + std::to_string(hr));
return false;
}
return true;
}
bool TriangleRenderer::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)
command_list->OMSetRenderTargets(1, &rtv_handle, FALSE, nullptr);
// Clear the render target by using the ClearRenderTargetView command
const float clearColor[] = THM_GREY;
command_list->ClearRenderTargetView(rtv_handle, clearColor, 0, nullptr);
// 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->DrawInstanced(6, 1, 0, 0); // finally draw 3 vertices (draw the 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;
}
bool TriangleRenderer::CreateVertexBuffer(ComPtr<ID3D12Device>& device, ComPtr<ID3D12GraphicsCommandList>& command_list,
Vertex* v_list, int vertex_buffer_size) {
// Sollte einmalig genutzt werden wenn Ressourcen geladen werden. Implementierung gemäß Vorlesung.
// create default heap
// default heap is memory on the GPU. Only the GPU has access to this memory
// To get data into this heap, we will have to upload the data using
// an upload heap
device->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), // a default heap
D3D12_HEAP_FLAG_NONE, // no flags
&CD3DX12_RESOURCE_DESC::Buffer(vertex_buffer_size), // resource description for a buffer
D3D12_RESOURCE_STATE_COPY_DEST, // we will start this heap in the copy destination state since we will copy data
// from the upload heap to this heap
nullptr,
// optimized clear value must be null for this type of resource. used for render targets and depth/stencil buffers
IID_PPV_ARGS(&m_vertex_buffer_));
// we can give resource heaps a name so when we debug with the graphics debugger we know what resource we are looking at
m_vertex_buffer_->SetName(L"Vertex Buffer Resource Heap");
// create upload heap
// upload heaps are used to upload data to the GPU. CPU can write to it, GPU can read from it
// We will upload the vertex buffer using this heap to the default heap
ID3D12Resource* vertexBufferUploadHeap;
device->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), // upload heap
D3D12_HEAP_FLAG_NONE, // no flags
&CD3DX12_RESOURCE_DESC::Buffer(vertex_buffer_size), // resource description for a buffer
D3D12_RESOURCE_STATE_GENERIC_READ, // GPU will read from this buffer and copy its contents to the default heap
nullptr,
IID_PPV_ARGS(&vertexBufferUploadHeap));
vertexBufferUploadHeap->SetName(L"Vertex Buffer Upload Resource Heap");
// store vertex buffer in upload heap
D3D12_SUBRESOURCE_DATA vertexData = {};
vertexData.pData = reinterpret_cast<BYTE*>(v_list); // pointer to our vertex array
vertexData.RowPitch = vertex_buffer_size; // size of all our triangle vertex data
vertexData.SlicePitch = vertex_buffer_size; // also the size of our triangle vertex data
// we are now creating a command with the command list to copy the data from
// the upload heap to the default heap
UINT64 r = UpdateSubresources(command_list.Get(), m_vertex_buffer_.Get(), vertexBufferUploadHeap, 0, 0, 1, &vertexData);
// transition the vertex buffer data from copy destination state to vertex buffer state
command_list->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_vertex_buffer_.Get(),
D3D12_RESOURCE_STATE_COPY_DEST,
D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER));
// create a vertex buffer view for the triangle. We get the GPU memory address to the vertex pointer using the GetGPUVirtualAddress() method
m_vertex_buffer_view_.BufferLocation = m_vertex_buffer_->GetGPUVirtualAddress();
m_vertex_buffer_view_.StrideInBytes = sizeof(Vertex);
m_vertex_buffer_view_.SizeInBytes = vertex_buffer_size;
return true;
}
bool TriangleRenderer::CompileShaders(ComPtr<ID3DBlob>& vertex_shader, LPCSTR entry_point_vertex_shader,
ComPtr<ID3D10Blob>& pixel_shader, LPCSTR entry_point_pixel_shader) {
// Sollte einmalig genutzt werden bevor das PSO gesetzt wird.
#if defined(_DEBUG)
// Enable better shader debugging with the graphics debugging tools.
UINT compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
#else
UINT compileFlags = 0;
#endif
HRESULT hr = D3DCompileFromFile(L"vertexShader.hlsl", nullptr, nullptr, entry_point_vertex_shader, "vs_5_0",
compileFlags, 0, &vertex_shader, nullptr);
if (FAILED(hr)) {
Log::Error("Error decompile vertex shader -ERROR:" + std::to_string(hr));
return false;
}
hr = D3DCompileFromFile(L"pixelShader.hlsl", nullptr, nullptr, entry_point_pixel_shader, "ps_5_0", compileFlags, 0,
&pixel_shader, nullptr);
if (FAILED(hr)) {
Log::Error("Error decompile pixel shader -ERROR:" + std::to_string(hr));
return false;
}
return true;
}
bool TriangleRenderer::CreateRootSignature(ComPtr<ID3D12Device>& device) {
// Sollte einmalig genutzt werden bevor das PSO gesetzt wird.
// Create an empty root signature
CD3DX12_ROOT_SIGNATURE_DESC rootSignatureDesc;
rootSignatureDesc.Init(0, nullptr, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
ComPtr<ID3DBlob> signature;
ComPtr<ID3DBlob> error;
HRESULT hr = D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error);
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;
}
void TriangleRenderer::Release() {
// Erst Resourcen dieser Klasse freigeben, dann die Resourcen der Basisklasse
m_root_signature_.Reset();
m_pipeline_state_.Reset();
m_vertex_buffer_.Reset();
Renderer::Release();
}