1 /*
2  * Copyright 2011-2019 Branimir Karadzic. All rights reserved.
3  * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
4  */
5 
6 #include "bgfx_p.h"
7 
8 #if BGFX_CONFIG_RENDERER_DIRECT3D11 || BGFX_CONFIG_RENDERER_DIRECT3D12
9 
10 #include "dxgi.h"
11 #include "renderer_d3d.h"
12 
13 #if !BX_PLATFORM_WINDOWS
14 #	include <inspectable.h>
15 #	if BX_PLATFORM_WINRT
16 #		include <windows.ui.xaml.media.dxinterop.h>
17 #	endif // BX_PLATFORM_WINRT
18 #endif // !BX_PLATFORM_WINDOWS
19 
20 namespace bgfx
21 {
22 	BX_PRAGMA_DIAGNOSTIC_PUSH();
23 	BX_PRAGMA_DIAGNOSTIC_IGNORED_GCC("-Wunused-variable");
24 	BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG("-Wunused-const-variable");
25 	BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG("-Wunneeded-internal-declaration");
26 
27 #if BX_PLATFORM_WINDOWS
28 	static PFN_CREATE_DXGI_FACTORY  CreateDXGIFactory;
29 	static PFN_GET_DEBUG_INTERFACE  DXGIGetDebugInterface;
30 	static PFN_GET_DEBUG_INTERFACE1 DXGIGetDebugInterface1;
31 #endif // BX_PLATFORM_WINDOWS
32 
33 	static const GUID IID_IDXGIFactory    = { 0x7b7166ec, 0x21c7, 0x44ae, { 0xb2, 0x1a, 0xc9, 0xae, 0x32, 0x1a, 0xe3, 0x69 } };
34 	static const GUID IID_IDXGIFactory2   = { 0x50c83a1c, 0xe072, 0x4c48, { 0x87, 0xb0, 0x36, 0x30, 0xfa, 0x36, 0xa6, 0xd0 } };
35 	static const GUID IID_IDXGIFactory3   = { 0x25483823, 0xcd46, 0x4c7d, { 0x86, 0xca, 0x47, 0xaa, 0x95, 0xb8, 0x37, 0xbd } };
36 	static const GUID IID_IDXGIFactory4   = { 0x1bc6ea02, 0xef36, 0x464f, { 0xbf, 0x0c, 0x21, 0xca, 0x39, 0xe5, 0x16, 0x8a } };
37 	static const GUID IID_IDXGIFactory5   = { 0x7632e1f5, 0xee65, 0x4dca, { 0x87, 0xfd, 0x84, 0xcd, 0x75, 0xf8, 0x83, 0x8d } };
38 	static const GUID IID_IDXGIDevice0    = { 0x54ec77fa, 0x1377, 0x44e6, { 0x8c, 0x32, 0x88, 0xfd, 0x5f, 0x44, 0xc8, 0x4c } };
39 	static const GUID IID_IDXGIDevice1    = { 0x77db970f, 0x6276, 0x48ba, { 0xba, 0x28, 0x07, 0x01, 0x43, 0xb4, 0x39, 0x2c } };
40 	static const GUID IID_IDXGIDevice2    = { 0x05008617, 0xfbfd, 0x4051, { 0xa7, 0x90, 0x14, 0x48, 0x84, 0xb4, 0xf6, 0xa9 } };
41 	static const GUID IID_IDXGIDevice3    = { 0x6007896c, 0x3244, 0x4afd, { 0xbf, 0x18, 0xa6, 0xd3, 0xbe, 0xda, 0x50, 0x23 } };
42 	static const GUID IID_IDXGIAdapter    = { 0x2411e7e1, 0x12ac, 0x4ccf, { 0xbd, 0x14, 0x97, 0x98, 0xe8, 0x53, 0x4d, 0xc0 } };
43 	static const GUID IID_IDXGIAdapter2   = { 0x0aa1ae0a, 0xfa0e, 0x4b84, { 0x86, 0x44, 0xe0, 0x5f, 0xf8, 0xe5, 0xac, 0xb5 } };
44 	static const GUID IID_IDXGIAdapter3   = { 0x645967a4, 0x1392, 0x4310, { 0xa7, 0x98, 0x80, 0x53, 0xce, 0x3e, 0x93, 0xfd } };
45 	static const GUID IID_IDXGIAdapter4   = { 0x3c8d99d1, 0x4fbf, 0x4181, { 0xa8, 0x2c, 0xaf, 0x66, 0xbf, 0x7b, 0xd2, 0x4e } };
46 	static const GUID IID_IDXGISwapChain3 = { 0x94d99bdb, 0xf1f8, 0x4ab0, { 0xb2, 0x36, 0x7d, 0xa0, 0x17, 0x0e, 0xda, 0xb1 } };
47 	static const GUID IID_IDXGISwapChain4 = { 0x3d585d5a, 0xbd4a, 0x489e, { 0xb1, 0xf4, 0x3d, 0xbc, 0xb6, 0x45, 0x2f, 0xfb } };
48 	static const GUID IID_IDXGIOutput6    = { 0x068346e8, 0xaaec, 0x4b84, { 0xad, 0xd7, 0x13, 0x7f, 0x51, 0x3f, 0x77, 0xa1 } };
49 
50 	BX_PRAGMA_DIAGNOSTIC_POP();
51 
52 	static const DXGI_COLOR_SPACE_TYPE s_colorSpace[] =
53 	{
54 		DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709,    // gamma 2.2,  BT.709
55 		DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709,    // gamma 1.0,  BT.709
56 		DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020, // gamma 2084, BT.2020
57 	};
58 
59 	static const char* s_colorSpaceStr[] =
60 	{
61 		// https://msdn.microsoft.com/en-us/library/windows/desktop/dn903661(v=vs.85).aspx
62 		"RGB,    0-255, 2.2,  Image, BT.709,  n/a",    // DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709
63 		"RGB,    0-255, 1.0,  Image, BT.709,  n/a",    // DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709
64 		"RGB,   16-235, 2.2,  Image, BT.709,  n/a",    // DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P709
65 		"RGB,   16-235, 2.2,  Image, BT.2020, n/a",    // DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P2020
66 		"Reserved",                                    // DXGI_COLOR_SPACE_RESERVED
67 		"YCbCr,  0-255, 2.2,  Image, BT.709,  BT.601", // DXGI_COLOR_SPACE_YCBCR_FULL_G22_NONE_P709_X601
68 		"YCbCr, 16-235, 2.2,  Video, BT.601,  n/a",    // DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601
69 		"YCbCr,  0-255, 2.2,  Video, BT.601,  n/a",    // DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601
70 		"YCbCr, 16-235, 2.2,  Video, BT.709,  n/a",    // DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709
71 		"YCbCr,  0-255, 2.2,  Video, BT.709,  n/a",    // DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709
72 		"YCbCr, 16-235, 2.2,  Video, BT.2020, n/a",    // DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020
73 		"YCbCr,  0-255, 2.2,  Video, BT.2020, n/a",    // DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020
74 		"RGB,    0-255, 2084, Image, BT.2020, n/a",    // DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020
75 		"YCbCr, 16-235, 2084, Video, BT.2020, n/a",    // DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_LEFT_P2020
76 		"RGB,    0-255, 2084, Image, BT.2020, n/a",    // DXGI_COLOR_SPACE_RGB_STUDIO_G2084_NONE_P2020
77 		"YCbCr, 16-235, 2.2,  Video, BT.2020, n/a",    // DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_TOPLEFT_P2020
78 		"YCbCr, 16-235, 2084, Video, BT.2020, n/a",    // DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_TOPLEFT_P2020
79 #if BX_PLATFORM_WINDOWS
80 		"RGB,    0-255, 2.2,  Image, BT.2020, n/a",    // DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020
81 		"YCbCr, 16-235, HLG,  Video, BT.2020, n/a",    // DXGI_COLOR_SPACE_YCBCR_STUDIO_GHLG_TOPLEFT_P2020
82 		"YCbCr,  0-255, HLG,  Video, BT.2020, n/a",    // DXGI_COLOR_SPACE_YCBCR_FULL_GHLG_TOPLEFT_P2020
83 //		"RGB,   16-235, 2.4,  Image, BT.709,  n/a",    // DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P709
84 //		"RGB,   16-235, 2.4,  Image, BT.2020, n/a",    // DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P2020
85 //		"YCbCr, 16-235, 2.4,  Video, BT.709,  n/a",    // DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_LEFT_P709
86 //		"YCbCr, 16-235, 2.4,  Video, BT.2020, n/a",    // DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_LEFT_P2020
87 //		"YCbCr, 16-235, 2.4,  Video, BT.2020, n/a",    // DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_TOPLEFT_P2020
88 #endif // BX_PLATFORM_WINDOWS
89 		"Custom",
90 	};
91 	static const DXGI_COLOR_SPACE_TYPE kDxgiLastColorSpace =
92 #if BX_PLATFORM_WINDOWS
93 		DXGI_COLOR_SPACE_YCBCR_FULL_GHLG_TOPLEFT_P2020
94 #else
95 		DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_TOPLEFT_P2020
96 #endif // BX_PLATFORM_WINDOWS
97 		;
98 	BX_STATIC_ASSERT(BX_COUNTOF(s_colorSpaceStr) == kDxgiLastColorSpace+2, "Colorspace string table mismatch with DXGI_COLOR_SPACE_*.");
99 
100 	static const GUID s_dxgiDeviceIIDs[] =
101 	{
102 		IID_IDXGIDevice3,
103 		IID_IDXGIDevice2,
104 		IID_IDXGIDevice1,
105 		IID_IDXGIDevice0,
106 	};
107 
108 	static const GUID s_dxgiSwapChainIIDs[] =
109 	{
110 		IID_IDXGISwapChain4,
111 		IID_IDXGISwapChain3,
112 	};
113 
DxgiSwapChain()114 	DxgiSwapChain::DxgiSwapChain()
115 	{
116 	}
117 
Dxgi()118 	Dxgi::Dxgi()
119 		: m_dxgiDll(NULL)
120 		, m_dxgiDebugDll(NULL)
121 		, m_driverType(D3D_DRIVER_TYPE_NULL)
122 		, m_factory(NULL)
123 		, m_adapter(NULL)
124 		, m_output(NULL)
125 	{
126 	}
127 
init(Caps & _caps)128 	bool Dxgi::init(Caps& _caps)
129 	{
130 #if BX_PLATFORM_WINDOWS
131 		m_dxgiDll = bx::dlopen("dxgi.dll");
132 		if (NULL == m_dxgiDll)
133 		{
134 			BX_TRACE("Init error: Failed to load dxgi.dll.");
135 			return false;
136 		}
137 
138 		m_dxgiDebugDll = bx::dlopen("dxgidebug.dll");
139 		if (NULL != m_dxgiDebugDll)
140 		{
141 			DXGIGetDebugInterface  = (PFN_GET_DEBUG_INTERFACE )bx::dlsym(m_dxgiDebugDll, "DXGIGetDebugInterface");
142 			DXGIGetDebugInterface1 = (PFN_GET_DEBUG_INTERFACE1)bx::dlsym(m_dxgiDebugDll, "DXGIGetDebugInterface1");
143 			if (NULL == DXGIGetDebugInterface
144 			&&  NULL == DXGIGetDebugInterface1)
145 			{
146 				bx::dlclose(m_dxgiDebugDll);
147 				m_dxgiDebugDll = NULL;
148 			}
149 		}
150 
151 		CreateDXGIFactory = (PFN_CREATE_DXGI_FACTORY)bx::dlsym(m_dxgiDll, "CreateDXGIFactory1");
152 
153 		if (NULL == CreateDXGIFactory)
154 		{
155 			CreateDXGIFactory = (PFN_CREATE_DXGI_FACTORY)bx::dlsym(m_dxgiDll, "CreateDXGIFactory");
156 		}
157 
158 		if (NULL == CreateDXGIFactory)
159 		{
160 			BX_TRACE("Init error: Function CreateDXGIFactory not found.");
161 			return false;
162 		}
163 #endif // BX_PLATFORM_WINDOWS
164 
165 		HRESULT hr = S_OK;
166 #if BX_PLATFORM_WINRT
167 		// WinRT requires the IDXGIFactory2 interface, which isn't supported on older platforms
168 		hr = CreateDXGIFactory1(IID_IDXGIFactory2, (void**)&m_factory);
169 #elif BX_PLATFORM_WINDOWS
170 		hr = CreateDXGIFactory(IID_IDXGIFactory, (void**)&m_factory);
171 #endif // BX_PLATFORM_*
172 
173 		if (FAILED(hr) )
174 		{
175 			BX_TRACE("Init error: Unable to create DXGI factory.");
176 			return false;
177 		}
178 
179 		m_driverType = BGFX_PCI_ID_SOFTWARE_RASTERIZER == _caps.vendorId
180 			? D3D_DRIVER_TYPE_WARP
181 			: D3D_DRIVER_TYPE_HARDWARE
182 			;
183 
184 		if (NULL != m_factory)
185 		{
186 			AdapterI* adapter;
187 			for (uint32_t ii = 0
188 				; DXGI_ERROR_NOT_FOUND != m_factory->EnumAdapters(ii, reinterpret_cast<IDXGIAdapter**>(&adapter) ) && ii < BX_COUNTOF(_caps.gpu)
189 				; ++ii
190 				)
191 			{
192 				{
193 					DXGI_ADAPTER_DESC desc;
194 					hr = adapter->GetDesc(&desc);
195 					if (SUCCEEDED(hr) )
196 					{
197 						BX_TRACE("Adapter #%d", ii);
198 
199 						char description[BX_COUNTOF(desc.Description)];
200 						wcstombs(description, desc.Description, BX_COUNTOF(desc.Description) );
201 						BX_TRACE("\tDescription: %s", description);
202 						BX_TRACE("\tVendorId: 0x%08x, DeviceId: 0x%08x, SubSysId: 0x%08x, Revision: 0x%08x"
203 							, desc.VendorId
204 							, desc.DeviceId
205 							, desc.SubSysId
206 							, desc.Revision
207 							);
208 						BX_TRACE("\tMemory: %" PRIi64 " (video), %" PRIi64 " (system), %" PRIi64 " (shared)"
209 							, desc.DedicatedVideoMemory
210 							, desc.DedicatedSystemMemory
211 							, desc.SharedSystemMemory
212 							);
213 
214 						_caps.gpu[ii].vendorId = (uint16_t)desc.VendorId;
215 						_caps.gpu[ii].deviceId = (uint16_t)desc.DeviceId;
216 						++_caps.numGPUs;
217 
218 						if (NULL == m_adapter)
219 						{
220 							if ( (BGFX_PCI_ID_NONE != _caps.vendorId ||             0 != _caps.deviceId)
221 							&&   (BGFX_PCI_ID_NONE == _caps.vendorId || desc.VendorId == _caps.vendorId)
222 							&&   (               0 == _caps.deviceId || desc.DeviceId == _caps.deviceId) )
223 							{
224 								m_adapter = adapter;
225 								m_adapter->AddRef();
226 								m_driverType = D3D_DRIVER_TYPE_UNKNOWN;
227 							}
228 
229 							if (BX_ENABLED(BGFX_CONFIG_DEBUG_PERFHUD)
230 							&&  !bx::strFind(description, "PerfHUD").isEmpty() )
231 							{
232 								m_adapter = adapter;
233 								m_driverType = D3D_DRIVER_TYPE_REFERENCE;
234 							}
235 						}
236 					}
237 				}
238 
239 				bool hdr10 = false;
240 
241 				IDXGIOutput* output;
242 				for (uint32_t jj = 0
243 					; SUCCEEDED(adapter->EnumOutputs(jj, &output) )
244 					; ++jj
245 					)
246 				{
247 					DXGI_OUTPUT_DESC outputDesc;
248 					hr = output->GetDesc(&outputDesc);
249 					if (SUCCEEDED(hr))
250 					{
251 						BX_TRACE("\tOutput #%d", jj);
252 
253 						char deviceName[BX_COUNTOF(outputDesc.DeviceName)];
254 						wcstombs(deviceName, outputDesc.DeviceName, BX_COUNTOF(outputDesc.DeviceName));
255 						BX_TRACE("\t\t           DeviceName: %s", deviceName);
256 						BX_TRACE("\t\t   DesktopCoordinates: %d, %d, %d, %d"
257 							, outputDesc.DesktopCoordinates.left
258 							, outputDesc.DesktopCoordinates.top
259 							, outputDesc.DesktopCoordinates.right
260 							, outputDesc.DesktopCoordinates.bottom
261 							);
262 						BX_TRACE("\t\t    AttachedToDesktop: %d", outputDesc.AttachedToDesktop);
263 						BX_TRACE("\t\t             Rotation: %d", outputDesc.Rotation);
264 
265 #if BX_PLATFORM_WINDOWS
266 						IDXGIOutput6* output6;
267 						hr = output->QueryInterface(IID_IDXGIOutput6, (void**)&output6);
268 						if (SUCCEEDED(hr) )
269 						{
270 							DXGI_OUTPUT_DESC1 desc;
271 							hr = output6->GetDesc1(&desc);
272 							if (SUCCEEDED(hr) )
273 							{
274 								BX_TRACE("\t\t         BitsPerColor: %d", desc.BitsPerColor);
275 								BX_TRACE("\t\t          Color space: %s (colorspace, range, gamma, sitting, primaries, transform)"
276 									, s_colorSpaceStr[bx::min<uint32_t>(desc.ColorSpace, kDxgiLastColorSpace+1)]
277 									);
278 								BX_TRACE("\t\t           RedPrimary: %f, %f", desc.RedPrimary[0],   desc.RedPrimary[1]);
279 								BX_TRACE("\t\t         GreenPrimary: %f, %f", desc.GreenPrimary[0], desc.GreenPrimary[1]);
280 								BX_TRACE("\t\t          BluePrimary: %f, %f", desc.BluePrimary[0],  desc.BluePrimary[1]);
281 								BX_TRACE("\t\t           WhitePoint: %f, %f", desc.WhitePoint[0],   desc.WhitePoint[1]);
282 								BX_TRACE("\t\t         MinLuminance: %f", desc.MinLuminance);
283 								BX_TRACE("\t\t         MaxLuminance: %f", desc.MaxLuminance);
284 								BX_TRACE("\t\tMaxFullFrameLuminance: %f", desc.MaxFullFrameLuminance);
285 								BX_TRACE("\t\t          HDR support: %s", DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 == desc.ColorSpace ? "true" : "false");
286 
287 								hdr10 |= DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 == desc.ColorSpace;
288 							}
289 
290 							// BK - warn only because RenderDoc might be present.
291 							DX_RELEASE_W(output6, 1);
292 						}
293 #endif // BX_PLATFORM_WINDOWS
294 
295 						DX_RELEASE(output, 0);
296 					}
297 				}
298 
299 				_caps.supported |= hdr10 ? BGFX_CAPS_HDR10 : 0;
300 
301 				DX_RELEASE(adapter, adapter == m_adapter ? 1 : 0);
302 			}
303 
304 			if (NULL == m_adapter)
305 			{
306 				hr = m_factory->EnumAdapters(0, reinterpret_cast<IDXGIAdapter**>(&m_adapter) );
307 				BX_WARN(SUCCEEDED(hr), "EnumAdapters failed 0x%08x.", hr);
308 				m_driverType = D3D_DRIVER_TYPE_UNKNOWN;
309 			}
310 
311 			bx::memSet(&m_adapterDesc, 0, sizeof(m_adapterDesc) );
312 			hr = m_adapter->GetDesc(&m_adapterDesc);
313 			BX_WARN(SUCCEEDED(hr), "Adapter GetDesc failed 0x%08x.", hr);
314 
315 			m_adapter->EnumOutputs(0, &m_output);
316 
317 			_caps.vendorId = 0 == m_adapterDesc.VendorId
318 				? BGFX_PCI_ID_SOFTWARE_RASTERIZER
319 				: (uint16_t)m_adapterDesc.VendorId
320 				;
321 			_caps.deviceId = (uint16_t)m_adapterDesc.DeviceId;
322 		}
323 
324 		return true;
325 	}
326 
shutdown()327 	void Dxgi::shutdown()
328 	{
329 		DX_RELEASE(m_output,  0);
330 		DX_RELEASE(m_adapter, 0);
331 		DX_RELEASE(m_factory, 0);
332 
333 		bx::dlclose(m_dxgiDebugDll);
334 		m_dxgiDebugDll = NULL;
335 
336 		bx::dlclose(m_dxgiDll);
337 		m_dxgiDll = NULL;
338 	}
339 
update(IUnknown * _device)340 	void Dxgi::update(IUnknown* _device)
341 	{
342 		IDXGIDevice* dxgiDevice = NULL;
343 		HRESULT hr = E_FAIL;
344 		for (uint32_t ii = 0; ii < BX_COUNTOF(s_dxgiDeviceIIDs) && FAILED(hr); ++ii)
345 		{
346 			hr = _device->QueryInterface(s_dxgiDeviceIIDs[ii], (void**)&dxgiDevice);
347 			BX_TRACE("DXGI device 11.%d, hr %x", BX_COUNTOF(s_dxgiDeviceIIDs) - 1 - ii, hr);
348 		}
349 
350 		if (NULL == m_factory)
351 		{
352 			DX_CHECK(dxgiDevice->GetAdapter(reinterpret_cast<IDXGIAdapter**>(&m_adapter) ) );
353 
354 			bx::memSet(&m_adapterDesc, 0, sizeof(m_adapterDesc) );
355 			hr = m_adapter->GetDesc(&m_adapterDesc);
356 			BX_WARN(SUCCEEDED(hr), "Adapter GetDesc failed 0x%08x.", hr);
357 
358 			DX_CHECK(m_adapter->GetParent(IID_IDXGIFactory2, (void**)&m_factory) );
359 		}
360 
361 		DX_RELEASE_I(dxgiDevice);
362 	}
363 
364 	static const GUID IID_ID3D12CommandQueue = { 0x0ec870a6, 0x5d7e, 0x4c22, { 0x8c, 0xfc, 0x5b, 0xaa, 0xe0, 0x76, 0x16, 0xed } };
365 
createSwapChain(IUnknown * _device,const SwapChainDesc & _scd,SwapChainI ** _swapChain)366 	HRESULT Dxgi::createSwapChain(IUnknown* _device, const SwapChainDesc& _scd, SwapChainI** _swapChain)
367 	{
368 		HRESULT hr = S_OK;
369 
370 		bool allowTearing = false;
371 
372 #if BX_PLATFORM_WINDOWS
373 		if (windowsVersionIs(Condition::GreaterEqual, 0x0604) )
374 		{
375 			// BK - CheckFeatureSupport with DXGI_FEATURE_PRESENT_ALLOW_TEARING
376 			//      will crash on pre Windows 8. Issue #1356.
377 			hr = m_factory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allowTearing, sizeof(allowTearing) );
378 			BX_TRACE("Allow tearing is %ssupported.", allowTearing ? "" : "not ");
379 		}
380 
381 		DXGI_SWAP_CHAIN_DESC scd;
382 		scd.BufferDesc.Width  = _scd.width;
383 		scd.BufferDesc.Height = _scd.height;
384 		scd.BufferDesc.RefreshRate.Numerator   = 1;
385 		scd.BufferDesc.RefreshRate.Denominator = 60;
386 		scd.BufferDesc.Format = _scd.format;
387 		scd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
388 		scd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
389 		scd.SampleDesc.Count   = 1;
390 		scd.SampleDesc.Quality = 0;
391 		scd.BufferUsage  = _scd.bufferUsage;
392 		scd.BufferCount  = _scd.bufferCount;
393 		scd.OutputWindow = (HWND)_scd.nwh;
394 		scd.Windowed     = _scd.windowed;
395 		scd.SwapEffect   = _scd.swapEffect;
396 		scd.Flags        = 0
397 			| _scd.flags
398 			| (allowTearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0)
399 			;
400 
401 		hr = m_factory->CreateSwapChain(
402 			  _device
403 			, &scd
404 			, reinterpret_cast<IDXGISwapChain**>(_swapChain)
405 			);
406 
407 		if (SUCCEEDED(hr) )
408 		{
409 			IDXGIDevice1* dxgiDevice1;
410 			_device->QueryInterface(IID_IDXGIDevice1, (void**)&dxgiDevice1);
411 			if (NULL != dxgiDevice1)
412 			{
413 				dxgiDevice1->SetMaximumFrameLatency(_scd.maxFrameLatency);
414 				DX_RELEASE_I(dxgiDevice1);
415 			}
416 		}
417 #else
418 		DXGI_SWAP_CHAIN_DESC1 scd;
419 		scd.Width  = _scd.width;
420 		scd.Height = _scd.height;
421 		scd.Format = _scd.format;
422 		scd.Stereo = _scd.stereo;
423 		scd.SampleDesc.Count   = 1;
424 		scd.SampleDesc.Quality = 0;
425 		scd.BufferUsage = _scd.bufferUsage;
426 		scd.BufferCount = _scd.bufferCount;
427 		scd.Scaling     = _scd.scaling;
428 		scd.SwapEffect  = _scd.swapEffect;
429 		scd.AlphaMode   = _scd.alphaMode;
430 		scd.Flags       = _scd.flags;
431 
432 		if (NULL == _scd.ndt)
433 		{
434 			hr = m_factory->CreateSwapChainForCoreWindow(
435 				  _device
436 				, (::IUnknown*)_scd.nwh
437 				, &scd
438 				, NULL
439 				, reinterpret_cast<IDXGISwapChain1**>(_swapChain)
440 				);
441 		}
442 		else if (reinterpret_cast<void*>(1) == _scd.ndt)
443 		{
444 			return E_FAIL;
445 		}
446 		else
447 		{
448 			hr = m_factory->CreateSwapChainForComposition(
449 				  _device
450 				, &scd
451 				, NULL
452 				, reinterpret_cast<IDXGISwapChain1**>(_swapChain)
453 				);
454 			if (FAILED(hr) )
455 			{
456 				return hr;
457 			}
458 
459 #	if BX_PLATFORM_WINRT
460 			IInspectable *nativeWindow = reinterpret_cast<IInspectable *>(_scd.nwh);
461 			ISwapChainBackgroundPanelNative* panel = NULL;
462 			hr = nativeWindow->QueryInterface(
463 				  __uuidof(ISwapChainBackgroundPanelNative)
464 				, (void **)&panel
465 				);
466 			if (FAILED(hr) )
467 			{
468 				return hr;
469 			}
470 
471 			if (NULL != panel)
472 			{
473 				hr = panel->SetSwapChain(*_swapChain);
474 				if (FAILED(hr) )
475 				{
476 					return hr;
477 				}
478 
479 				panel->Release();
480 			}
481 #	endif // BX_PLATFORM_WINRT
482 		}
483 #endif // BX_PLATFORM_WINDOWS
484 
485 		if (FAILED(hr) )
486 		{
487 			BX_TRACE("Failed to create swap chain.");
488 			return hr;
489 		}
490 
491 #if BX_PLATFORM_WINDOWS
492 		if (SUCCEEDED(hr) )
493 		{
494 			for (uint32_t ii = 0; ii < BX_COUNTOF(s_dxgiSwapChainIIDs); ++ii)
495 			{
496 				IDXGISwapChain1* swapChain;
497 				hr = (*_swapChain)->QueryInterface(s_dxgiSwapChainIIDs[ii], (void**)&swapChain);
498 				BX_TRACE("DXGI swap chain %d, hr %x", 4-ii, hr);
499 
500 				if (SUCCEEDED(hr) )
501 				{
502 					DX_RELEASE(*_swapChain, 1);
503 					*_swapChain = reinterpret_cast<SwapChainI*>(swapChain);
504 
505 					BX_TRACE("Color space support:");
506 					for (uint32_t jj = 0; jj < BX_COUNTOF(s_colorSpace); ++jj)
507 					{
508 						uint32_t colorSpaceSupport;
509 						reinterpret_cast<IDXGISwapChain3*>(*_swapChain)->CheckColorSpaceSupport(s_colorSpace[jj], &colorSpaceSupport);
510 						BX_TRACE("\t%2d: \"%-20s\", 0x%08x, %s"
511 							, s_colorSpace[jj]
512 							, s_colorSpaceStr[s_colorSpace[jj]]
513 							, colorSpaceSupport
514 							, 0 != (colorSpaceSupport & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT)
515 							? "supported"
516 							: "-"
517 							);
518 					}
519 
520 					break;
521 				}
522 			}
523 		}
524 #endif // BX_PLATFORM_WINDOWS
525 
526 		updateHdr10(*_swapChain, _scd);
527 
528 		return S_OK;
529 	}
530 
updateHdr10(SwapChainI * _swapChain,const SwapChainDesc & _scd)531 	void Dxgi::updateHdr10(SwapChainI* _swapChain, const SwapChainDesc& _scd)
532 	{
533 #if BX_PLATFORM_WINDOWS
534 		::IDXGISwapChain4* swapChain4;
535 		HRESULT hr = _swapChain->QueryInterface(IID_IDXGISwapChain4, (void**)&swapChain4);
536 
537 		if (SUCCEEDED(hr) )
538 		{
539 			const DXGI_COLOR_SPACE_TYPE colorSpace =
540 				  _scd.format == DXGI_FORMAT_R10G10B10A2_UNORM  ? DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020
541 				: _scd.format == DXGI_FORMAT_R16G16B16A16_FLOAT ? DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709
542 				:                                                 DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709
543 				;
544 
545 			hr = swapChain4->SetColorSpace1(colorSpace);
546 
547 			if (SUCCEEDED(hr) )
548 			{
549 				DXGI_OUTPUT_DESC1 desc;
550 
551 				IDXGIOutput* output;
552 				hr = _swapChain->GetContainingOutput(&output);
553 				if (SUCCEEDED(hr) )
554 				{
555 					IDXGIOutput6* output6;
556 					hr = output->QueryInterface(IID_IDXGIOutput6, (void**)&output6);
557 					if (SUCCEEDED(hr) )
558 					{
559 						hr = output6->GetDesc1(&desc);
560 						if (SUCCEEDED(hr) )
561 						{
562 							BX_TRACE("Display specs:")
563 							BX_TRACE("\t         BitsPerColor: %d", desc.BitsPerColor);
564 							BX_TRACE("\t          Color space: %s (colorspace, range, gamma, sitting, primaries, transform)"
565 								, s_colorSpaceStr[bx::min<uint32_t>(desc.ColorSpace, kDxgiLastColorSpace+1)]
566 								);
567 							BX_TRACE("\t           RedPrimary: %f, %f", desc.RedPrimary[0],   desc.RedPrimary[1]);
568 							BX_TRACE("\t         GreenPrimary: %f, %f", desc.GreenPrimary[0], desc.GreenPrimary[1]);
569 							BX_TRACE("\t          BluePrimary: %f, %f", desc.BluePrimary[0],  desc.BluePrimary[1]);
570 							BX_TRACE("\t           WhitePoint: %f, %f", desc.WhitePoint[0],   desc.WhitePoint[1]);
571 							BX_TRACE("\t         MinLuminance: %f", desc.MinLuminance);
572 							BX_TRACE("\t         MaxLuminance: %f", desc.MaxLuminance);
573 							BX_TRACE("\tMaxFullFrameLuminance: %f", desc.MaxFullFrameLuminance);
574 						}
575 
576 						DX_RELEASE(output6, 1);
577 					}
578 
579 					DX_RELEASE(output, 0);
580 				}
581 
582 				DXGI_HDR_METADATA_HDR10 hdr10;
583 				hdr10.RedPrimary[0]   = uint16_t(desc.RedPrimary[0]   * 50000.0f);
584 				hdr10.RedPrimary[1]   = uint16_t(desc.RedPrimary[1]   * 50000.0f);
585 				hdr10.GreenPrimary[0] = uint16_t(desc.GreenPrimary[0] * 50000.0f);
586 				hdr10.GreenPrimary[1] = uint16_t(desc.GreenPrimary[1] * 50000.0f);
587 				hdr10.BluePrimary[0]  = uint16_t(desc.BluePrimary[0]  * 50000.0f);
588 				hdr10.BluePrimary[1]  = uint16_t(desc.BluePrimary[1]  * 50000.0f);
589 				hdr10.WhitePoint[0]   = uint16_t(desc.WhitePoint[0]   * 50000.0f);
590 				hdr10.WhitePoint[1]   = uint16_t(desc.WhitePoint[1]   * 50000.0f);
591 				hdr10.MaxMasteringLuminance     = uint32_t(desc.MaxLuminance * 10000.0f);
592 				hdr10.MinMasteringLuminance     = uint32_t(desc.MinLuminance * 10000.0f);
593 				hdr10.MaxContentLightLevel      = uint16_t(desc.MaxFullFrameLuminance);
594 				hdr10.MaxFrameAverageLightLevel = uint16_t(desc.MaxFullFrameLuminance);
595 				hr = swapChain4->SetHDRMetaData(DXGI_HDR_METADATA_TYPE_HDR10, sizeof(DXGI_HDR_METADATA_HDR10), &hdr10);
596 			}
597 
598 			DX_RELEASE(swapChain4, 1);
599 		}
600 #else
601 		BX_UNUSED(_swapChain, _scd);
602 #endif // BX_PLATFORM_WINDOWS
603 	}
604 
resizeBuffers(SwapChainI * _swapChain,const SwapChainDesc & _scd,const uint32_t * _nodeMask,IUnknown * const * _presentQueue)605 	HRESULT Dxgi::resizeBuffers(SwapChainI* _swapChain, const SwapChainDesc& _scd, const uint32_t* _nodeMask, IUnknown* const* _presentQueue)
606 	{
607 		HRESULT hr;
608 
609 #if BX_PLATFORM_WINDOWS
610 		if (NULL != _nodeMask
611 		&&  NULL != _presentQueue)
612 		{
613 			hr = _swapChain->ResizeBuffers1(
614 				  _scd.bufferCount
615 				, _scd.width
616 				, _scd.height
617 				, _scd.format
618 				, _scd.flags
619 				, _nodeMask
620 				, _presentQueue
621 				);
622 		}
623 		else
624 #endif // BX_PLATFORM_WINDOWS
625 		{
626 			BX_UNUSED(_nodeMask, _presentQueue);
627 
628 			hr = _swapChain->ResizeBuffers(
629 				  _scd.bufferCount
630 				, _scd.width
631 				, _scd.height
632 				, _scd.format
633 				, _scd.flags
634 				);
635 		}
636 
637 		if (SUCCEEDED(hr) )
638 		{
639 			updateHdr10(_swapChain, _scd);
640 		}
641 
642 		return hr;
643 	}
644 
trim()645 	void Dxgi::trim()
646 	{
647 #if BX_PLATFORM_WINDOWS || BX_PLATFORM_WINRT
648 		IDXGIDevice3* device;
649 		HRESULT hr = m_factory->QueryInterface(IID_IDXGIDevice3, (void**)&device);
650 		if (SUCCEEDED(hr) )
651 		{
652 			device->Trim();
653 			DX_RELEASE(device, 1);
654 		}
655 #endif // BX_PLATFORM_WINDOWS || BX_PLATFORM_WINRT
656 	}
657 
658 } // namespace bgfx
659 
660 #endif // BGFX_CONFIG_RENDERER_DIRECT3D11 || BGFX_CONFIG_RENDERER_DIRECT3D12
661