forked from vixorien/D3D11Starter
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGraphics.cpp
More file actions
474 lines (404 loc) · 15.7 KB
/
Copy pathGraphics.cpp
File metadata and controls
474 lines (404 loc) · 15.7 KB
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
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
#include "Graphics.h"
#include <dxgi1_6.h>
// Needed for a helper function to load pre-compiled shader files
#pragma comment(lib, "d3dcompiler.lib")
#include <d3dcompiler.h>
#include "PathHelpers.h"
// Tell the drivers to use high-performance GPU in multi-GPU systems (like laptops)
extern "C"
{
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; // NVIDIA
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; // AMD
}
namespace Graphics
{
// Annonymous namespace to hold variables
// only accessible in this file
namespace
{
bool apiInitialized = false;
bool supportsTearing = false;
bool vsyncDesired = false;
BOOL isFullscreen = false;
D3D_FEATURE_LEVEL featureLevel{};
// Size of the constant buffer heap (measured in bytes)
unsigned int cbHeapSizeInBytes = 0;
// Position of the next unused portion of the heap
unsigned int cbHeapOffsetInBytes = 0;
Microsoft::WRL::ComPtr<ID3D11DeviceContext1> context1;
}
}
// Getters
bool Graphics::VsyncState() { return vsyncDesired || !supportsTearing || isFullscreen; }
std::wstring Graphics::APIName()
{
switch (featureLevel)
{
case D3D_FEATURE_LEVEL_10_0: return L"D3D10";
case D3D_FEATURE_LEVEL_10_1: return L"D3D10.1";
case D3D_FEATURE_LEVEL_11_0: return L"D3D11";
case D3D_FEATURE_LEVEL_11_1: return L"D3D11.1";
default: return L"Unknown";
}
}
// --------------------------------------------------------
// Initializes the Graphics API, which requires window details.
//
// windowWidth - Width of the window (and our viewport)
// windowHeight - Height of the window (and our viewport)
// windowHandle - OS-level handle of the window
// vsyncIfPossible - Sync to the monitor's refresh rate if available?
// --------------------------------------------------------
HRESULT Graphics::Initialize(unsigned int windowWidth, unsigned int windowHeight, HWND windowHandle, bool vsyncIfPossible)
{
// Only initialize once
if (apiInitialized)
return E_FAIL;
// Save desired vsync state, though it may be stuck "on" if
// the device doesn't support screen tearing
vsyncDesired = vsyncIfPossible;
// Determine if screen tearing ("vsync off") is available
// - This is necessary due to variable refresh rate displays
Microsoft::WRL::ComPtr<IDXGIFactory5> factory;
if (SUCCEEDED(CreateDXGIFactory1(IID_PPV_ARGS(&factory))))
{
// Check for this specific feature (must use BOOL typedef here!)
BOOL tearingSupported = false;
HRESULT featureCheck = factory->CheckFeatureSupport(
DXGI_FEATURE_PRESENT_ALLOW_TEARING,
&tearingSupported,
sizeof(tearingSupported));
// Final determination of support
supportsTearing = SUCCEEDED(featureCheck) && tearingSupported;
}
// This will hold options for DirectX initialization
unsigned int deviceFlags = 0;
#if defined(DEBUG) || defined(_DEBUG)
// If we're in debug mode in visual studio, we also
// want to make a "Debug DirectX Device" to see some
// errors and warnings in Visual Studio's output window
// when things go wrong!
deviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
// Create a description of how our swap
// chain should work
DXGI_SWAP_CHAIN_DESC swapDesc = {};
swapDesc.BufferCount = 2;
swapDesc.BufferDesc.Width = windowWidth;
swapDesc.BufferDesc.Height = windowHeight;
swapDesc.BufferDesc.RefreshRate.Numerator = 60;
swapDesc.BufferDesc.RefreshRate.Denominator = 1;
swapDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swapDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
swapDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapDesc.Flags = supportsTearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0;
swapDesc.OutputWindow = windowHandle;
swapDesc.SampleDesc.Count = 1;
swapDesc.SampleDesc.Quality = 0;
swapDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
swapDesc.Windowed = true;
// Result variable for below function calls
HRESULT hr = S_OK;
// Attempt to initialize Direct3D 11.1
D3D_FEATURE_LEVEL level_11_1 = D3D_FEATURE_LEVEL_11_1;
hr = D3D11CreateDeviceAndSwapChain(
0, // Video adapter (physical GPU) to use, or null for default
D3D_DRIVER_TYPE_HARDWARE, // We want to use the hardware (GPU)
0, // Used when doing software rendering
deviceFlags, // Any special options
&level_11_1, // Specifying DirectX 11.1 only
1, // The number of feature levels in the above param
D3D11_SDK_VERSION, // Current version of the SDK
&swapDesc, // Address of swap chain options
SwapChain.GetAddressOf(), // Pointer to our Swap Chain pointer
(ID3D11Device**)Device.GetAddressOf(), // Pointer to our Device pointer
&featureLevel, // Retrieve exact API feature level in use
(ID3D11DeviceContext**)Context.GetAddressOf()); // Pointer to our Device Context pointer
// Did 11.1 initialization fail? If so, try 11.0
if (hr == E_INVALIDARG)
{
hr = D3D11CreateDeviceAndSwapChain(
0, // Video adapter (physical GPU) to use, or null for default
D3D_DRIVER_TYPE_HARDWARE, // We want to use the hardware (GPU)
0, // Used when doing software rendering
deviceFlags, // Any special options
0, // Leave out the array of options to use the highest version available
0, // The number of feature levels in the above param
D3D11_SDK_VERSION, // Current version of the SDK
&swapDesc, // Address of swap chain options
SwapChain.GetAddressOf(), // Pointer to our Swap Chain pointer
(ID3D11Device**)Device.GetAddressOf(), // Pointer to our Device pointer
&featureLevel, // Retrieve exact API feature level in use
(ID3D11DeviceContext**)Context.GetAddressOf()); // Pointer to our Device Context pointer
}
// If it's still a failure, we're unable to initialize any version of Direct3D
if (FAILED(hr))
{
return hr;
}
// We're set up
apiInitialized = true;
// Call ResizeBuffers(), which will also set up the
// render target view and depth stencil view for the
// various buffers we need for rendering. This call
// will also set the appropriate viewport.
ResizeBuffers(windowWidth, windowHeight);
// Ring buffer size
cbHeapSizeInBytes = 1000 * 256;
// Ensure 256-byte alignment in the event the above calculation changes
cbHeapSizeInBytes = (cbHeapSizeInBytes + 255) / 256 * 256;
// Buffer description
D3D11_BUFFER_DESC cbd = {};
cbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
cbd.ByteWidth = cbHeapSizeInBytes;
cbd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
cbd.MiscFlags = 0;
cbd.StructureByteStride = 0;
cbd.Usage = D3D11_USAGE_DYNAMIC;
// Create the buffer
Graphics::Device->CreateBuffer(&cbd, 0, ConstantBufferHeap.GetAddressOf());
// Grab the Direct3D 11.1 version of the context for later
Context->QueryInterface<ID3D11DeviceContext1>(context1.GetAddressOf());
#if defined(DEBUG) || defined(_DEBUG)
// If we're in debug mode, set up the info queue to
// get debug messages we can print to our console
Microsoft::WRL::ComPtr<ID3D11Debug> debug;
Device->QueryInterface(IID_PPV_ARGS(debug.GetAddressOf()));
debug->QueryInterface(IID_PPV_ARGS(InfoQueue.GetAddressOf()));
#endif
return S_OK;
}
// --------------------------------------------------------
// Called at the end of the program to clean up any
// graphics API specific memory.
//
// This exists for completeness since D3D objects generally
// use ComPtrs, which get cleaned up automatically. Other
// APIs might need more explicit clean up.
// --------------------------------------------------------
void Graphics::ShutDown()
{
}
// --------------------------------------------------------
// When the window is resized, the underlying
// buffers (textures) must also be resized to match.
//
// If we don't do this, the window size and our rendering
// resolution won't match up. This can result in odd
// stretching/skewing.
//
// width - New width of the window (and our viewport)
// height - New height of the window (and our viewport)
// --------------------------------------------------------
void Graphics::ResizeBuffers(unsigned int width, unsigned int height)
{
// Ensure graphics API is initialized
if (!apiInitialized)
return;
BackBufferRTV.Reset();
DepthBufferDSV.Reset();
// Resize the swap chain buffers
SwapChain->ResizeBuffers(
2,
width,
height,
DXGI_FORMAT_R8G8B8A8_UNORM,
supportsTearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0);
// Grab the references to the first buffer
Microsoft::WRL::ComPtr<ID3D11Texture2D> backBufferTexture;
SwapChain->GetBuffer(
0,
__uuidof(ID3D11Texture2D),
(void**)backBufferTexture.GetAddressOf());
// Now that we have the texture, create a render target view
// for the back buffer so we can render into it.
Device->CreateRenderTargetView(
backBufferTexture.Get(),
0,
BackBufferRTV.GetAddressOf());
// Set up the description of the texture to use for the depth buffer
D3D11_TEXTURE2D_DESC depthStencilDesc = {};
depthStencilDesc.Width = width;
depthStencilDesc.Height = height;
depthStencilDesc.MipLevels = 1;
depthStencilDesc.ArraySize = 1;
depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
depthStencilDesc.Usage = D3D11_USAGE_DEFAULT;
depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
depthStencilDesc.CPUAccessFlags = 0;
depthStencilDesc.MiscFlags = 0;
depthStencilDesc.SampleDesc.Count = 1;
depthStencilDesc.SampleDesc.Quality = 0;
// Create the depth buffer and its view, then
// release our reference to the texture
Microsoft::WRL::ComPtr<ID3D11Texture2D> depthBufferTexture;
Device->CreateTexture2D(&depthStencilDesc, 0, &depthBufferTexture);
Device->CreateDepthStencilView(
depthBufferTexture.Get(),
0,
DepthBufferDSV.GetAddressOf());
// Bind the views to the pipeline, so rendering properly
// uses their underlying textures
Context->OMSetRenderTargets(
1,
BackBufferRTV.GetAddressOf(), // This requires a pointer to a pointer (an array of pointers), so we get the address of the pointer
DepthBufferDSV.Get());
// Lastly, set up a viewport so we render into
// to correct portion of the window
D3D11_VIEWPORT viewport = {};
viewport.TopLeftX = 0;
viewport.TopLeftY = 0;
viewport.Width = (float)width;
viewport.Height = (float)height;
viewport.MinDepth = 0.0f;
viewport.MaxDepth = 1.0f;
Context->RSSetViewports(1, &viewport);
// Are we in a fullscreen state?
SwapChain->GetFullscreenState(&isFullscreen, 0);
}
// --------------------------------------------------------
// Shader loaders
// --------------------------------------------------------
Microsoft::WRL::ComPtr<ID3D11PixelShader> Graphics::LoadPixelShader(std::wstring filePath)
{
// BLOBs (or Binary Large OBjects) for reading raw data from external files
// - This is a simplified way of handling big chunks of external data
// - Literally just a big array of bytes read from a file
ID3DBlob* pixelShaderBlob;
// Temporary variable to hold the resulting shader
Microsoft::WRL::ComPtr<ID3D11PixelShader> pixelShader;
// Loading shaders
// - Visual Studio will compile our shaders at build time
// - They are saved as .cso (Compiled Shader Object) files
// - We need to load them when the application starts
// Read our compiled shader code files into blobs
// - Essentially just "open the file and plop its contents here"
// - Uses the custom FixPath() helper from Helpers.h to ensure relative paths
D3DReadFileToBlob(FixPath(filePath).c_str(), &pixelShaderBlob);
// Create the actual Direct3D shaders on the GPU
Graphics::Device->CreatePixelShader(
pixelShaderBlob->GetBufferPointer(), // Pointer to blob's contents
pixelShaderBlob->GetBufferSize(), // How big is that data?
0, // No classes in this shader
pixelShader.GetAddressOf()); // Address of the ID3D11PixelShader pointer
return pixelShader;
}
Microsoft::WRL::ComPtr<ID3D11VertexShader> Graphics::LoadVertexShader(std::wstring filePath)
{
// Used to store raw data from external files in blob
ID3DBlob* vertexShaderBlob;
// Temporary variable to hold the resulting shader
Microsoft::WRL::ComPtr<ID3D11VertexShader> vertexShader;
// Read the compiled shader code file into a blob
D3DReadFileToBlob(FixPath(filePath).c_str(), &vertexShaderBlob);
// Create the actual Direct3D vertex shader on the GPU
Graphics::Device->CreateVertexShader(
vertexShaderBlob->GetBufferPointer(), // Get a pointer to the blob's contents
vertexShaderBlob->GetBufferSize(), // How big is that data?
0, // No classes in this shader
vertexShader.GetAddressOf()); // The address of the ID3D11VertexShader pointer
return vertexShader;
}
// --------------------------------------------------------
// Copies the data into the next unused portion of the
// constant buffer heap then binds the buffer to the
// specified shader stage of the pipeline
//
// data - The data to copy to the GPU
// dataSizeInBytes - The byte size of the data to copy
// shaderType - The shader stage for binding
// registerSlot - The slot for binding
// --------------------------------------------------------
void Graphics::FillAndBindNextConstantBuffer(void* data, unsigned int dataSizeInBytes, D3D11_SHADER_TYPE shaderType, unsigned int registerSlot)
{
// How much space will we actually need? Each chunk must be
// a multiple of 256 bytes.
unsigned int reservationSize = (dataSizeInBytes + 255) / 256 * 256;
// Does this fit in the remaining space? If not, loop back to
// the beginning of the ring buffer
if (cbHeapOffsetInBytes + reservationSize >= cbHeapSizeInBytes)
cbHeapOffsetInBytes = 0;
D3D11_MAPPED_SUBRESOURCE map{};
Context->Map(
ConstantBufferHeap.Get(),
0,
D3D11_MAP_WRITE_NO_OVERWRITE,
0,
&map);
// Write into the proper portion of the buffer
void* uploadAddress = reinterpret_cast<void*>((UINT64)map.pData + cbHeapOffsetInBytes);
memcpy(uploadAddress, data, dataSizeInBytes);
// Unmap as soon as the copy is done
Context->Unmap(ConstantBufferHeap.Get(), 0);
// Calculate the binding offset and size as measured in 16-byte constants
unsigned int firstConstant = cbHeapOffsetInBytes / 16;
unsigned int numConstants = reservationSize / 16;
// Bind the buffer to the proper pipeline stage
switch (shaderType)
{
case D3D11_VERTEX_SHADER:
context1->VSSetConstantBuffers1(
registerSlot,
1,
ConstantBufferHeap.GetAddressOf(),
&firstConstant,
&numConstants);
break;
case D3D11_PIXEL_SHADER:
context1->PSSetConstantBuffers1(
registerSlot,
1,
ConstantBufferHeap.GetAddressOf(),
&firstConstant,
&numConstants);
break;
}
// Offset for the next call
cbHeapOffsetInBytes += reservationSize;
}
// --------------------------------------------------------
// Prints graphics debug messages waiting in the queue
// --------------------------------------------------------
void Graphics::PrintDebugMessages()
{
// Do we actually have an info queue (usually in debug mode)
if (!InfoQueue)
return;
// Any messages?
UINT64 messageCount = InfoQueue->GetNumStoredMessages();
if (messageCount == 0)
return;
// Loop and print messages
for (UINT64 i = 0; i < messageCount; i++)
{
// Get the size so we can reserve space
size_t messageSize = 0;
InfoQueue->GetMessage(i, 0, &messageSize);
// Reserve space for this message
D3D11_MESSAGE* message = (D3D11_MESSAGE*)malloc(messageSize);
InfoQueue->GetMessage(i, message, &messageSize);
// Print and clean up memory
if (message)
{
// Color code based on severity
switch (message->Severity)
{
case D3D11_MESSAGE_SEVERITY_CORRUPTION:
case D3D11_MESSAGE_SEVERITY_ERROR:
printf("\x1B[91m"); break; // RED
case D3D11_MESSAGE_SEVERITY_WARNING:
printf("\x1B[93m"); break; // YELLOW
case D3D11_MESSAGE_SEVERITY_INFO:
case D3D11_MESSAGE_SEVERITY_MESSAGE:
printf("\x1B[96m"); break; // CYAN
}
printf("%s\n\n", message->pDescription);
free(message);
// Reset color
printf("\x1B[0m");
}
}
// Clear any messages we've printed
InfoQueue->ClearStoredMessages();
}