1 /*
2  * Copyright 2011-2019 Branimir Karadzic. All rights reserved.
3  * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
4  */
5 
6 #ifndef BGFX_RENDERER_D3D12_H_HEADER_GUARD
7 #define BGFX_RENDERER_D3D12_H_HEADER_GUARD
8 
9 #define USE_D3D12_DYNAMIC_LIB BX_PLATFORM_WINDOWS
10 
11 #include <sal.h>
12 #if BX_PLATFORM_WINDOWS || BX_PLATFORM_WINRT
13 #   include <d3d12.h>
14 #else
15 #   if !BGFX_CONFIG_DEBUG
16 #      define D3DCOMPILE_NO_DEBUG 1
17 #   endif // !BGFX_CONFIG_DEBUG
18 #   include <d3d12_x.h>
19 #endif // BX_PLATFORM_XBOXONE
20 
21 #if defined(__MINGW32__) // BK - temp workaround for MinGW until I nuke d3dx12 usage.
22 extern "C++" {
23 	__extension__ template<typename Ty>
24 	const GUID& __mingw_uuidof();
25 
26 	template<>
27 	const GUID& __mingw_uuidof<ID3D12Device>()
28 	{
29 		static const GUID IID_ID3D12Device0 = { 0x189819f1, 0x1db6, 0x4b57, { 0xbe, 0x54, 0x18, 0x21, 0x33, 0x9b, 0x85, 0xf7 } };
30 		return IID_ID3D12Device0;
31 	}
32 }
33 #endif // defined(__MINGW32__)
34 
35 BX_PRAGMA_DIAGNOSTIC_PUSH();
36 BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wmissing-field-initializers");
37 #if BX_PLATFORM_XBOXONE
38 #	include <d3dx12_x.h>
39 #else
40 #	include <d3dx12.h>
41 #endif // BX_PLATFORM_XBOXONE
42 BX_PRAGMA_DIAGNOSTIC_POP();
43 
44 #ifndef D3D12_TEXTURE_DATA_PITCH_ALIGNMENT
45 #	define D3D12_TEXTURE_DATA_PITCH_ALIGNMENT 1024
46 #endif // D3D12_TEXTURE_DATA_PITCH_ALIGNMENT
47 
48 #include "renderer.h"
49 #include "renderer_d3d.h"
50 #include "shader_dxbc.h"
51 #include "debug_renderdoc.h"
52 #include "nvapi.h"
53 #include "dxgi.h"
54 
55 #if BGFX_CONFIG_DEBUG_ANNOTATION
56 #	if BX_PLATFORM_WINDOWS || BX_PLATFORM_WINRT
57 typedef struct PIXEventsThreadInfo* (WINAPI* PFN_PIX_GET_THREAD_INFO)();
58 typedef uint64_t                    (WINAPI* PFN_PIX_EVENTS_REPLACE_BLOCK)(bool _getEarliestTime);
59 
60 extern PFN_PIX_GET_THREAD_INFO      bgfx_PIXGetThreadInfo;
61 extern PFN_PIX_EVENTS_REPLACE_BLOCK bgfx_PIXEventsReplaceBlock;
62 
63 #		define PIXGetThreadInfo      bgfx_PIXGetThreadInfo
64 #		define PIXEventsReplaceBlock bgfx_PIXEventsReplaceBlock
65 #	else
66 extern "C" struct PIXEventsThreadInfo* WINAPI bgfx_PIXGetThreadInfo();
67 extern "C" uint64_t                    WINAPI bgfx_PIXEventsReplaceBlock(bool _getEarliestTime);
68 #	endif // BX_PLATFORM_WINDOWS
69 
70 #	include <pix3.h>
71 
72 #	define _PIX3_BEGINEVENT(_commandList, _color, _name) PIXBeginEvent(_commandList, _color, _name)
73 #	define _PIX3_SETMARKER(_commandList, _color, _name)  PIXSetMarker(_commandList, _color, _name)
74 #	define _PIX3_ENDEVENT(_commandList)                  PIXEndEvent(_commandList)
75 
76 #	define PIX3_BEGINEVENT(_commandList, _color, _name) _PIX3_BEGINEVENT(_commandList, _color, _name)
77 #	define PIX3_SETMARKER(_commandList, _color, _name)  _PIX3_SETMARKER(_commandList, _color, _name)
78 #	define PIX3_ENDEVENT(_commandList)                  _PIX3_ENDEVENT(_commandList)
79 #else
80 #	define PIX3_BEGINEVENT(_commandList, _color, _name) BX_UNUSED(_commandList, _color, _name)
81 #	define PIX3_SETMARKER(_commandList, _color, _name)  BX_UNUSED(_commandList, _color, _name)
82 #	define PIX3_ENDEVENT(_commandList)                  BX_UNUSED(_commandList)
83 #endif // BGFX_CONFIG_DEBUG_ANNOTATION
84 
85 #define BGFX_D3D12_PROFILER_BEGIN(_view, _abgr)                   \
86 	BX_MACRO_BLOCK_BEGIN                                          \
87 		PIX3_BEGINEVENT(m_commandList, _abgr, s_viewName[_view]); \
88 		BGFX_PROFILER_BEGIN(s_viewName[view], _abgr);             \
89 	BX_MACRO_BLOCK_END
90 
91 #define BGFX_D3D12_PROFILER_BEGIN_LITERAL(_name, _abgr)           \
92 	BX_MACRO_BLOCK_BEGIN                                          \
93 		PIX3_BEGINEVENT(m_commandList, _abgr, "" # _name);        \
94 		BGFX_PROFILER_BEGIN_LITERAL("" # _name, _abgr);           \
95 	BX_MACRO_BLOCK_END
96 
97 #define BGFX_D3D12_PROFILER_END()     \
98 	BX_MACRO_BLOCK_BEGIN              \
99 		BGFX_PROFILER_END();          \
100 		PIX3_ENDEVENT(m_commandList); \
101 	BX_MACRO_BLOCK_END
102 
103 namespace bgfx { namespace d3d12
104 {
105 	typedef HRESULT (WINAPI* PFN_D3D12_ENABLE_EXPERIMENTAL_FEATURES)(uint32_t _numFeatures, const IID* _iids, void* _configurationStructs, uint32_t* _configurationStructSizes);
106 
107 	struct Rdt
108 	{
109 		enum Enum
110 		{
111 			Sampler,
112 			SRV,
113 			CBV,
114 			UAV,
115 
116 			Count
117 		};
118 	};
119 
120 	class ScratchBufferD3D12
121 	{
122 	public:
ScratchBufferD3D12()123 		ScratchBufferD3D12()
124 		{
125 		}
126 
~ScratchBufferD3D12()127 		~ScratchBufferD3D12()
128 		{
129 		}
130 
131 		void create(uint32_t _size, uint32_t _maxDescriptors);
132 		void destroy();
133 		void reset(D3D12_GPU_DESCRIPTOR_HANDLE& _gpuHandle);
134 
135 		void  allocEmpty(D3D12_GPU_DESCRIPTOR_HANDLE& _gpuHandle);
136 
137 		void* allocCbv(D3D12_GPU_VIRTUAL_ADDRESS& _gpuAddress, uint32_t _size);
138 
139 		void  allocSrv(D3D12_GPU_DESCRIPTOR_HANDLE& _gpuHandle, struct TextureD3D12& _texture, uint8_t _mip = 0);
140 		void  allocSrv(D3D12_GPU_DESCRIPTOR_HANDLE& _gpuHandle, struct BufferD3D12& _buffer);
141 
142 		void  allocUav(D3D12_GPU_DESCRIPTOR_HANDLE& _gpuHandle, struct TextureD3D12& _texture, uint8_t _mip = 0);
143 		void  allocUav(D3D12_GPU_DESCRIPTOR_HANDLE& _gpuHandle, struct BufferD3D12& _buffer);
144 
getHeap()145 		ID3D12DescriptorHeap* getHeap()
146 		{
147 			return m_heap;
148 		}
149 
150 	private:
151 		ID3D12DescriptorHeap* m_heap;
152 		ID3D12Resource* m_upload;
153 		D3D12_GPU_VIRTUAL_ADDRESS m_gpuVA;
154 		D3D12_CPU_DESCRIPTOR_HANDLE m_cpuHandle;
155 		D3D12_GPU_DESCRIPTOR_HANDLE m_gpuHandle;
156 		uint32_t m_incrementSize;
157 		uint8_t* m_data;
158 		uint32_t m_size;
159 		uint32_t m_pos;
160 	};
161 
162 	class DescriptorAllocatorD3D12
163 	{
164 	public:
DescriptorAllocatorD3D12()165 		DescriptorAllocatorD3D12()
166 			: m_numDescriptorsPerBlock(1)
167 		{
168 		}
169 
~DescriptorAllocatorD3D12()170 		~DescriptorAllocatorD3D12()
171 		{
172 		}
173 
174 		void create(D3D12_DESCRIPTOR_HEAP_TYPE _type, uint16_t _maxDescriptors, uint16_t _numDescriptorsPerBlock = 1);
175 		void destroy();
176 
177 		uint16_t alloc(ID3D12Resource* _ptr, const D3D12_SHADER_RESOURCE_VIEW_DESC* _desc);
178 		uint16_t alloc(const uint32_t* _flags, uint32_t _num, const float _palette[][4]);
179 		void free(uint16_t _handle);
180 		void reset();
181 
182 		D3D12_GPU_DESCRIPTOR_HANDLE get(uint16_t _handle);
183 
getHeap()184 		ID3D12DescriptorHeap* getHeap()
185 		{
186 			return m_heap;
187 		}
188 
189 	private:
190 		ID3D12DescriptorHeap* m_heap;
191 		bx::HandleAlloc* m_handleAlloc;
192 		D3D12_CPU_DESCRIPTOR_HANDLE m_cpuHandle;
193 		D3D12_GPU_DESCRIPTOR_HANDLE m_gpuHandle;
194 		uint32_t m_incrementSize;
195 		uint16_t m_numDescriptorsPerBlock;
196 	};
197 
198 	struct BufferD3D12
199 	{
BufferD3D12BufferD3D12200 		BufferD3D12()
201 			: m_ptr(NULL)
202 			, m_state(D3D12_RESOURCE_STATE_COMMON)
203 			, m_size(0)
204 			, m_flags(BGFX_BUFFER_NONE)
205 			, m_dynamic(false)
206 		{
207 		}
208 
209 		void create(uint32_t _size, void* _data, uint16_t _flags, bool _vertex, uint32_t _stride = 0);
210 		void update(ID3D12GraphicsCommandList* _commandList, uint32_t _offset, uint32_t _size, void* _data, bool _discard = false);
211 		void destroy();
212 
213 		D3D12_RESOURCE_STATES setState(ID3D12GraphicsCommandList* _commandList, D3D12_RESOURCE_STATES _state);
214 
215 		D3D12_SHADER_RESOURCE_VIEW_DESC  m_srvd;
216 		D3D12_UNORDERED_ACCESS_VIEW_DESC m_uavd;
217 		ID3D12Resource* m_ptr;
218 		D3D12_GPU_VIRTUAL_ADDRESS m_gpuVA;
219 		D3D12_RESOURCE_STATES m_state;
220 		uint32_t m_size;
221 		uint16_t m_flags;
222 		bool m_dynamic;
223 	};
224 
225 	struct VertexBufferD3D12 : public BufferD3D12
226 	{
227 		void create(uint32_t _size, void* _data, VertexLayoutHandle _layoutHandle, uint16_t _flags);
228 
229 		VertexLayoutHandle m_layoutHandle;
230 	};
231 
232 	struct ShaderD3D12
233 	{
ShaderD3D12ShaderD3D12234 		ShaderD3D12()
235 			: m_code(NULL)
236 			, m_constantBuffer(NULL)
237 			, m_hash(0)
238 			, m_numUniforms(0)
239 			, m_numPredefined(0)
240 		{
241 		}
242 
243 		void create(const Memory* _mem);
244 
destroyShaderD3D12245 		void destroy()
246 		{
247 			if (NULL != m_constantBuffer)
248 			{
249 				UniformBuffer::destroy(m_constantBuffer);
250 				m_constantBuffer = NULL;
251 			}
252 
253 			m_numPredefined = 0;
254 
255 			if (NULL != m_code)
256 			{
257 				release(m_code);
258 				m_code = NULL;
259 				m_hash = 0;
260 			}
261 		}
262 
263 		const Memory* m_code;
264 		UniformBuffer* m_constantBuffer;
265 
266 		PredefinedUniform m_predefined[PredefinedUniform::Count];
267 		uint16_t m_attrMask[Attrib::Count];
268 
269 		uint32_t m_hash;
270 		uint16_t m_numUniforms;
271 		uint16_t m_size;
272 		uint8_t m_numPredefined;
273 	};
274 
275 	struct ProgramD3D12
276 	{
ProgramD3D12ProgramD3D12277 		ProgramD3D12()
278 			: m_vsh(NULL)
279 			, m_fsh(NULL)
280 		{
281 		}
282 
createProgramD3D12283 		void create(const ShaderD3D12* _vsh, const ShaderD3D12* _fsh)
284 		{
285 			BX_CHECK(NULL != _vsh->m_code, "Vertex shader doesn't exist.");
286 			m_vsh = _vsh;
287 			bx::memCopy(&m_predefined[0], _vsh->m_predefined, _vsh->m_numPredefined*sizeof(PredefinedUniform));
288 			m_numPredefined = _vsh->m_numPredefined;
289 
290 			if (NULL != _fsh)
291 			{
292 				BX_CHECK(NULL != _fsh->m_code, "Fragment shader doesn't exist.");
293 				m_fsh = _fsh;
294 				bx::memCopy(&m_predefined[m_numPredefined], _fsh->m_predefined, _fsh->m_numPredefined*sizeof(PredefinedUniform));
295 				m_numPredefined += _fsh->m_numPredefined;
296 			}
297 		}
298 
destroyProgramD3D12299 		void destroy()
300 		{
301 			m_numPredefined = 0;
302 			m_vsh = NULL;
303 			m_fsh = NULL;
304 		}
305 
306 		const ShaderD3D12* m_vsh;
307 		const ShaderD3D12* m_fsh;
308 
309 		PredefinedUniform m_predefined[PredefinedUniform::Count * 2];
310 		uint8_t m_numPredefined;
311 	};
312 
313 	struct TextureD3D12
314 	{
315 		enum Enum
316 		{
317 			Texture2D,
318 			Texture3D,
319 			TextureCube,
320 		};
321 
TextureD3D12TextureD3D12322 		TextureD3D12()
323 			: m_ptr(NULL)
324 			, m_directAccessPtr(NULL)
325 			, m_state(D3D12_RESOURCE_STATE_COMMON)
326 			, m_numMips(0)
327 		{
328 			bx::memSet(&m_srvd, 0, sizeof(m_srvd) );
329 			bx::memSet(&m_uavd, 0, sizeof(m_uavd) );
330 		}
331 
332 		void* create(const Memory* _mem, uint64_t _flags, uint8_t _skip);
333 		void destroy();
334 		void update(ID3D12GraphicsCommandList* _commandList, uint8_t _side, uint8_t _mip, const Rect& _rect, uint16_t _z, uint16_t _depth, uint16_t _pitch, const Memory* _mem);
335 		void resolve(uint8_t _resolve) const;
336 		D3D12_RESOURCE_STATES setState(ID3D12GraphicsCommandList* _commandList, D3D12_RESOURCE_STATES _state);
337 
338 		D3D12_SHADER_RESOURCE_VIEW_DESC  m_srvd;
339 		D3D12_UNORDERED_ACCESS_VIEW_DESC m_uavd;
340 		ID3D12Resource* m_ptr;
341 		void* m_directAccessPtr;
342 		D3D12_RESOURCE_STATES m_state;
343 		uint64_t m_flags;
344 		uint32_t m_width;
345 		uint32_t m_height;
346 		uint32_t m_depth;
347 		uint32_t m_numLayers;
348 		uint16_t m_samplerIdx;
349 		uint8_t m_type;
350 		uint8_t m_requestedFormat;
351 		uint8_t m_textureFormat;
352 		uint8_t m_numMips;
353 	};
354 
355 	struct FrameBufferD3D12
356 	{
FrameBufferD3D12FrameBufferD3D12357 		FrameBufferD3D12()
358 			: m_swapChain(NULL)
359 			, m_nwh(NULL)
360 			, m_width(0)
361 			, m_height(0)
362 			, m_denseIdx(UINT16_MAX)
363 			, m_num(0)
364 			, m_numTh(0)
365 			, m_state(D3D12_RESOURCE_STATE_PRESENT)
366 			, m_needPresent(false)
367 		{
368 			m_depth.idx = bgfx::kInvalidHandle;
369 		}
370 
371 		void create(uint8_t _num, const Attachment* _attachment);
372 		void create(uint16_t _denseIdx, void* _nwh, uint32_t _width, uint32_t _height, TextureFormat::Enum _format, TextureFormat::Enum _depthFormat);
373 		uint16_t destroy();
374 		HRESULT present(uint32_t _syncInterval, uint32_t _flags);
375 		void preReset();
376 		void postReset();
377 		void resolve();
378 		void clear(ID3D12GraphicsCommandList* _commandList, const Clear& _clear, const float _palette[][4], const D3D12_RECT* _rect = NULL, uint32_t _num = 0);
379 		D3D12_RESOURCE_STATES setState(ID3D12GraphicsCommandList* _commandList, uint8_t _idx, D3D12_RESOURCE_STATES _state);
380 
381 		TextureHandle m_texture[BGFX_CONFIG_MAX_FRAME_BUFFER_ATTACHMENTS];
382 		TextureHandle m_depth;
383 		Dxgi::SwapChainI* m_swapChain;
384 		void* m_nwh;
385 		uint32_t m_width;
386 		uint32_t m_height;
387 		uint16_t m_denseIdx;
388 		uint8_t m_num;
389 		uint8_t m_numTh;
390 		Attachment m_attachment[BGFX_CONFIG_MAX_FRAME_BUFFER_ATTACHMENTS];
391 		D3D12_RESOURCE_STATES m_state;
392 		bool m_needPresent;
393 	};
394 
395 	struct CommandQueueD3D12
396 	{
CommandQueueD3D12CommandQueueD3D12397 		CommandQueueD3D12()
398 			: m_currentFence(0)
399 			, m_completedFence(0)
400 			, m_control(BX_COUNTOF(m_commandList) )
401 		{
402 			BX_STATIC_ASSERT(BX_COUNTOF(m_commandList) == BX_COUNTOF(m_release) );
403 		}
404 
405 		void init(ID3D12Device* _device);
406 		void shutdown();
407 		ID3D12GraphicsCommandList* alloc();
408 		uint64_t kick();
409 		void finish(uint64_t _waitFence = UINT64_MAX, bool _finishAll = false);
410 		bool tryFinish(uint64_t _waitFence);
411 		void release(ID3D12Resource* _ptr);
412 		bool consume(uint32_t _ms = INFINITE);
413 
414 		struct CommandList
415 		{
416 			ID3D12GraphicsCommandList* m_commandList;
417 			ID3D12CommandAllocator* m_commandAllocator;
418 			HANDLE m_event;
419 		};
420 
421 		ID3D12CommandQueue* m_commandQueue;
422 		uint64_t m_currentFence;
423 		uint64_t m_completedFence;
424 		ID3D12Fence* m_fence;
425 		CommandList m_commandList[256];
426 		typedef stl::vector<ID3D12Resource*> ResourceArray;
427 		ResourceArray m_release[256];
428 		bx::RingBufferControl m_control;
429 	};
430 
431 	struct BatchD3D12
432 	{
433 		enum Enum
434 		{
435 			Draw,
436 			DrawIndexed,
437 
438 			Count
439 		};
440 
BatchD3D12BatchD3D12441 		BatchD3D12()
442 			: m_currIndirect(0)
443 			, m_maxDrawPerBatch(0)
444 			, m_minIndirect(0)
445 			, m_flushPerBatch(0)
446 		{
447 			bx::memSet(m_num, 0, sizeof(m_num) );
448 		}
449 
~BatchD3D12BatchD3D12450 		~BatchD3D12()
451 		{
452 		}
453 
454 		void create(uint32_t _maxDrawPerBatch);
455 		void destroy();
456 
457 		template<typename Ty>
458 		Ty& getCmd(Enum _type);
459 
460 		uint32_t draw(ID3D12GraphicsCommandList* _commandList, D3D12_GPU_VIRTUAL_ADDRESS _cbv, const RenderDraw& _draw);
461 
462 		void flush(ID3D12GraphicsCommandList* _commandList, Enum _type);
463 		void flush(ID3D12GraphicsCommandList* _commandList, bool _clean = false);
464 
465 		void begin();
466 		void end(ID3D12GraphicsCommandList* _commandList);
467 
setSeqModeBatchD3D12468 		void setSeqMode(bool _enabled)
469 		{
470 			m_flushPerBatch = _enabled ? 1 : m_maxDrawPerBatch;
471 		}
472 
setIndirectModeBatchD3D12473 		void setIndirectMode(bool _enabled)
474 		{
475 			m_minIndirect = _enabled ? 64 : UINT32_MAX;
476 		}
477 
478 		ID3D12CommandSignature* m_commandSignature[Count];
479 		uint32_t m_num[Count];
480 		void* m_cmds[Count];
481 
482 		struct DrawIndirectCommand
483 		{
484 			D3D12_VERTEX_BUFFER_VIEW vbv[BGFX_CONFIG_MAX_VERTEX_STREAMS + 1 /* instanced buffer */];
485 			D3D12_GPU_VIRTUAL_ADDRESS cbv;
486 			D3D12_DRAW_ARGUMENTS args;
487 		};
488 
489 		struct DrawIndexedIndirectCommand
490 		{
491 			D3D12_VERTEX_BUFFER_VIEW vbv[BGFX_CONFIG_MAX_VERTEX_STREAMS + 1 /* instanced buffer */];
492 			D3D12_INDEX_BUFFER_VIEW ibv;
493 			D3D12_GPU_VIRTUAL_ADDRESS cbv;
494 			D3D12_DRAW_INDEXED_ARGUMENTS args;
495 		};
496 
497 		struct Stats
498 		{
499 			uint32_t m_numImmediate[Count];
500 			uint32_t m_numIndirect[Count];
501 		};
502 
503 		BufferD3D12 m_indirect[32];
504 		uint32_t m_currIndirect;
505 		DrawIndexedIndirectCommand m_current;
506 
507 		Stats m_stats;
508 		uint32_t m_maxDrawPerBatch;
509 		uint32_t m_minIndirect;
510 		uint32_t m_flushPerBatch;
511 	};
512 
513 	struct TimerQueryD3D12
514 	{
TimerQueryD3D12TimerQueryD3D12515 		TimerQueryD3D12()
516 			: m_control(BX_COUNTOF(m_query) )
517 		{
518 		}
519 
520 		void init();
521 		void shutdown();
522 		uint32_t begin(uint32_t _resultIdx);
523 		void end(uint32_t _idx);
524 		bool update();
525 
526 		struct Query
527 		{
528 			uint32_t m_resultIdx;
529 			bool     m_ready;
530 			uint64_t m_fence;
531 		};
532 
533 		struct Result
534 		{
resetTimerQueryD3D12::Result535 			void reset()
536 			{
537 				m_begin     = 0;
538 				m_end       = 0;
539 				m_pending   = 0;
540 			}
541 
542 			uint64_t m_begin;
543 			uint64_t m_end;
544 			uint32_t m_pending;
545 		};
546 
547 		uint64_t m_frequency;
548 
549 		Result m_result[BGFX_CONFIG_MAX_VIEWS+1];
550 		Query m_query[BGFX_CONFIG_MAX_VIEWS*4];
551 
552 		ID3D12Resource*  m_readback;
553 		ID3D12QueryHeap* m_queryHeap;
554 		uint64_t* m_queryResult;
555 		bx::RingBufferControl m_control;
556 	};
557 
558 	struct OcclusionQueryD3D12
559 	{
OcclusionQueryD3D12OcclusionQueryD3D12560 		OcclusionQueryD3D12()
561 			: m_control(BX_COUNTOF(m_handle) )
562 		{
563 		}
564 
565 		void init();
566 		void shutdown();
567 		void begin(ID3D12GraphicsCommandList* _commandList, Frame* _render, OcclusionQueryHandle _handle);
568 		void end(ID3D12GraphicsCommandList* _commandList);
569 		void invalidate(OcclusionQueryHandle _handle);
570 
571 		ID3D12Resource*  m_readback;
572 		ID3D12QueryHeap* m_queryHeap;
573 		OcclusionQueryHandle m_handle[BGFX_CONFIG_MAX_OCCLUSION_QUERIES];
574 		uint64_t* m_result;
575 		bx::RingBufferControl m_control;
576 	};
577 
578 } /* namespace d3d12 */ } // namespace bgfx
579 
580 #endif // BGFX_RENDERER_D3D12_H_HEADER_GUARD
581