1 #include "pch.h"
2 #include <algorithm>
3 #include "DeviceResources.h"
4 #include "DirectXHelper.h"
5 
6 using namespace D2D1;
7 using namespace DirectX;
8 using namespace Microsoft::WRL;
9 using namespace Windows::Foundation;
10 using namespace Windows::Graphics::Display;
11 using namespace Windows::UI::Core;
12 using namespace Windows::UI::Xaml::Controls;
13 using namespace Platform;
14 
15 namespace DisplayMetrics
16 {
17 	// High resolution displays can require a lot of GPU and battery power to render.
18 	// High resolution phones, for example, may suffer from poor battery life if
19 	// games attempt to render at 60 frames per second at full fidelity.
20 	// The decision to render at full fidelity across all platforms and form factors
21 	// should be deliberate.
22 	static const bool SupportHighResolutions = true;
23 
24 	// The default thresholds that define a "high resolution" display. If the thresholds
25 	// are exceeded and SupportHighResolutions is false, the dimensions will be scaled
26 	// by 50%.
27 	static const float DpiThreshold = 192.0f;		// 200% of standard desktop display.
28 	static const float WidthThreshold = 1920.0f;	// 1080p width.
29 	static const float HeightThreshold = 1080.0f;	// 1080p height.
30 };
31 
32 // Constants used to calculate screen rotations
33 namespace ScreenRotation
34 {
35 	// 0-degree Z-rotation
36 	static const XMFLOAT4X4 Rotation0(
37 		1.0f, 0.0f, 0.0f, 0.0f,
38 		0.0f, 1.0f, 0.0f, 0.0f,
39 		0.0f, 0.0f, 1.0f, 0.0f,
40 		0.0f, 0.0f, 0.0f, 1.0f
41 		);
42 
43 	// 90-degree Z-rotation
44 	static const XMFLOAT4X4 Rotation90(
45 		0.0f, 1.0f, 0.0f, 0.0f,
46 		-1.0f, 0.0f, 0.0f, 0.0f,
47 		0.0f, 0.0f, 1.0f, 0.0f,
48 		0.0f, 0.0f, 0.0f, 1.0f
49 		);
50 
51 	// 180-degree Z-rotation
52 	static const XMFLOAT4X4 Rotation180(
53 		-1.0f, 0.0f, 0.0f, 0.0f,
54 		0.0f, -1.0f, 0.0f, 0.0f,
55 		0.0f, 0.0f, 1.0f, 0.0f,
56 		0.0f, 0.0f, 0.0f, 1.0f
57 		);
58 
59 	// 270-degree Z-rotation
60 	static const XMFLOAT4X4 Rotation270(
61 		0.0f, -1.0f, 0.0f, 0.0f,
62 		1.0f, 0.0f, 0.0f, 0.0f,
63 		0.0f, 0.0f, 1.0f, 0.0f,
64 		0.0f, 0.0f, 0.0f, 1.0f
65 		);
66 };
67 
68 // Constructor for DeviceResources.
DeviceResources()69 DX::DeviceResources::DeviceResources() :
70 	m_screenViewport(),
71 	m_d3dFeatureLevel(D3D_FEATURE_LEVEL_9_1),
72 	m_d3dRenderTargetSize(),
73 	m_outputSize(),
74 	m_logicalSize(),
75 	m_nativeOrientation(DisplayOrientations::None),
76 	m_currentOrientation(DisplayOrientations::None),
77 	m_dpi(-1.0f),
78 	m_effectiveDpi(-1.0f),
79 	m_deviceNotify(nullptr)
80 {
81 	CreateDeviceIndependentResources();
82 	CreateDeviceResources();
83 }
84 
85 // Configures resources that don't depend on the Direct3D device.
CreateDeviceIndependentResources()86 void DX::DeviceResources::CreateDeviceIndependentResources()
87 {
88 	// Initialize Direct2D resources.
89 	D2D1_FACTORY_OPTIONS options;
90 	ZeroMemory(&options, sizeof(D2D1_FACTORY_OPTIONS));
91 
92 #if defined(_DEBUG)
93 	// If the project is in a debug build, enable Direct2D debugging via SDK Layers.
94 	options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
95 #endif
96 
97 	// Initialize the Direct2D Factory.
98 	DX::ThrowIfFailed(
99 		D2D1CreateFactory(
100 			D2D1_FACTORY_TYPE_SINGLE_THREADED,
101 			__uuidof(ID2D1Factory3),
102 			&options,
103 			&m_d2dFactory
104 			)
105 		);
106 
107 	// Initialize the DirectWrite Factory.
108 	DX::ThrowIfFailed(
109 		DWriteCreateFactory(
110 			DWRITE_FACTORY_TYPE_SHARED,
111 			__uuidof(IDWriteFactory3),
112 			&m_dwriteFactory
113 			)
114 		);
115 
116 	// Initialize the Windows Imaging Component (WIC) Factory.
117 	DX::ThrowIfFailed(
118 		CoCreateInstance(
119 			CLSID_WICImagingFactory2,
120 			nullptr,
121 			CLSCTX_INPROC_SERVER,
122 			IID_PPV_ARGS(&m_wicFactory)
123 			)
124 		);
125 }
126 
127 // Configures the Direct3D device, and stores handles to it and the device context.
CreateDeviceResources()128 void DX::DeviceResources::CreateDeviceResources()
129 {
130 	// This flag adds support for surfaces with a different color channel ordering
131 	// than the API default. It is required for compatibility with Direct2D.
132 	UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
133 
134 #if defined(_DEBUG)
135 	if (DX::SdkLayersAvailable())
136 	{
137 		// If the project is in a debug build, enable debugging via SDK Layers with this flag.
138 		creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
139 	}
140 #endif
141 
142 	// This array defines the set of DirectX hardware feature levels this app will support.
143 	// Note the ordering should be preserved.
144 	// Don't forget to declare your application's minimum required feature level in its
145 	// description.  All applications are assumed to support 9.1 unless otherwise stated.
146 	D3D_FEATURE_LEVEL featureLevels[] =
147 	{
148 		D3D_FEATURE_LEVEL_12_1,
149 		D3D_FEATURE_LEVEL_12_0,
150 		D3D_FEATURE_LEVEL_11_1,
151 		D3D_FEATURE_LEVEL_11_0,
152 		D3D_FEATURE_LEVEL_10_1,
153 		D3D_FEATURE_LEVEL_10_0,
154 		D3D_FEATURE_LEVEL_9_3,
155 		D3D_FEATURE_LEVEL_9_2,
156 		D3D_FEATURE_LEVEL_9_1
157 	};
158 
159 	// Create the Direct3D 11 API device object and a corresponding context.
160 	ComPtr<ID3D11Device> device;
161 	ComPtr<ID3D11DeviceContext> context;
162 
163 	HRESULT hr = D3D11CreateDevice(
164 		nullptr,					// Specify nullptr to use the default adapter.
165 		D3D_DRIVER_TYPE_HARDWARE,	// Create a device using the hardware graphics driver.
166 		0,							// Should be 0 unless the driver is D3D_DRIVER_TYPE_SOFTWARE.
167 		creationFlags,				// Set debug and Direct2D compatibility flags.
168 		featureLevels,				// List of feature levels this app can support.
169 		ARRAYSIZE(featureLevels),	// Size of the list above.
170 		D3D11_SDK_VERSION,			// Always set this to D3D11_SDK_VERSION for Windows Store apps.
171 		&device,					// Returns the Direct3D device created.
172 		&m_d3dFeatureLevel,			// Returns feature level of device created.
173 		&context					// Returns the device immediate context.
174 		);
175 
176 	if (FAILED(hr))
177 	{
178 		// If the initialization fails, fall back to the WARP device.
179 		// For more information on WARP, see:
180 		// http://go.microsoft.com/fwlink/?LinkId=286690
181 		DX::ThrowIfFailed(
182 			D3D11CreateDevice(
183 				nullptr,
184 				D3D_DRIVER_TYPE_WARP, // Create a WARP device instead of a hardware device.
185 				0,
186 				creationFlags,
187 				featureLevels,
188 				ARRAYSIZE(featureLevels),
189 				D3D11_SDK_VERSION,
190 				&device,
191 				&m_d3dFeatureLevel,
192 				&context
193 				)
194 			);
195 	}
196 
197 	// Store pointers to the Direct3D 11.3 API device and immediate context.
198 	DX::ThrowIfFailed(
199 		device.As(&m_d3dDevice)
200 		);
201 
202 	DX::ThrowIfFailed(
203 		context.As(&m_d3dContext)
204 		);
205 
206 	// Create the Direct2D device object and a corresponding context.
207 	ComPtr<IDXGIDevice3> dxgiDevice;
208 	DX::ThrowIfFailed(
209 		m_d3dDevice.As(&dxgiDevice)
210 		);
211 
212 	DX::ThrowIfFailed(
213 		m_d2dFactory->CreateDevice(dxgiDevice.Get(), &m_d2dDevice)
214 		);
215 
216 	DX::ThrowIfFailed(
217 		m_d2dDevice->CreateDeviceContext(
218 			D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
219 			&m_d2dContext
220 			)
221 		);
222 }
223 
224 // These resources need to be recreated every time the window size is changed.
CreateWindowSizeDependentResources()225 void DX::DeviceResources::CreateWindowSizeDependentResources()
226 {
227 	// Clear the previous window size specific context.
228 	ID3D11RenderTargetView* nullViews[] = {nullptr};
229 	m_d3dContext->OMSetRenderTargets(ARRAYSIZE(nullViews), nullViews, nullptr);
230 	m_d3dRenderTargetView = nullptr;
231 	m_d2dContext->SetTarget(nullptr);
232 	m_d2dTargetBitmap = nullptr;
233 	m_d3dContext->Flush1(D3D11_CONTEXT_TYPE_ALL, nullptr);
234 
235 	UpdateRenderTargetSize();
236 
237 	// The width and height of the swap chain must be based on the window's
238 	// natively-oriented width and height. If the window is not in the native
239 	// orientation, the dimensions must be reversed.
240 	DXGI_MODE_ROTATION displayRotation = ComputeDisplayRotation();
241 
242 	bool swapDimensions = displayRotation == DXGI_MODE_ROTATION_ROTATE90 || displayRotation == DXGI_MODE_ROTATION_ROTATE270;
243 	m_d3dRenderTargetSize.Width = swapDimensions ? m_outputSize.Height : m_outputSize.Width;
244 	m_d3dRenderTargetSize.Height = swapDimensions ? m_outputSize.Width : m_outputSize.Height;
245 
246 	if (m_swapChain != nullptr)
247 	{
248 		// If the swap chain already exists, resize it.
249 		HRESULT hr = m_swapChain->ResizeBuffers(
250 			2, // Double-buffered swap chain.
251 			lround(m_d3dRenderTargetSize.Width),
252 			lround(m_d3dRenderTargetSize.Height),
253 			DXGI_FORMAT_B8G8R8A8_UNORM,
254 			0
255 			);
256 
257 		if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
258 		{
259 			// If the device was removed for any reason, a new device and swap chain will need to be created.
260 			HandleDeviceLost();
261 
262 			// Everything is set up now. Do not continue execution of this method. HandleDeviceLost will reenter this method
263 			// and correctly set up the new device.
264 			return;
265 		}
266 		else
267 		{
268 			DX::ThrowIfFailed(hr);
269 		}
270 	}
271 	else
272 	{
273 		// Otherwise, create a new one using the same adapter as the existing Direct3D device.
274 		DXGI_SCALING scaling = DisplayMetrics::SupportHighResolutions ? DXGI_SCALING_NONE : DXGI_SCALING_STRETCH;
275 		DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
276 
277 		swapChainDesc.Width = lround(m_d3dRenderTargetSize.Width);		// Match the size of the window.
278 		swapChainDesc.Height = lround(m_d3dRenderTargetSize.Height);
279 		swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;				// This is the most common swap chain format.
280 		swapChainDesc.Stereo = false;
281 		swapChainDesc.SampleDesc.Count = 1;								// Don't use multi-sampling.
282 		swapChainDesc.SampleDesc.Quality = 0;
283 		swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
284 		swapChainDesc.BufferCount = 2;									// Use double-buffering to minimize latency.
285 		swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;	// All Windows Store apps must use this SwapEffect.
286 		swapChainDesc.Flags = 0;
287 		swapChainDesc.Scaling = scaling;
288 		swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
289 
290 		// This sequence obtains the DXGI factory that was used to create the Direct3D device above.
291 		ComPtr<IDXGIDevice3> dxgiDevice;
292 		DX::ThrowIfFailed(
293 			m_d3dDevice.As(&dxgiDevice)
294 			);
295 
296 		ComPtr<IDXGIAdapter> dxgiAdapter;
297 		DX::ThrowIfFailed(
298 			dxgiDevice->GetAdapter(&dxgiAdapter)
299 			);
300 
301 		ComPtr<IDXGIFactory4> dxgiFactory;
302 		DX::ThrowIfFailed(
303 			dxgiAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory))
304 			);
305 
306 		ComPtr<IDXGISwapChain1> swapChain;
307 		DX::ThrowIfFailed(
308 			dxgiFactory->CreateSwapChainForCoreWindow(
309 				m_d3dDevice.Get(),
310 				reinterpret_cast<IUnknown*>(m_window.Get()),
311 				&swapChainDesc,
312 				nullptr,
313 				&swapChain
314 				)
315 			);
316 		DX::ThrowIfFailed(
317 			swapChain.As(&m_swapChain)
318 			);
319 
320 		// Ensure that DXGI does not queue more than one frame at a time. This both reduces latency and
321 		// ensures that the application will only render after each VSync, minimizing power consumption.
322 		DX::ThrowIfFailed(
323 			dxgiDevice->SetMaximumFrameLatency(1)
324 			);
325 	}
326 
327 	// Set the proper orientation for the swap chain, and generate 2D and
328 	// 3D matrix transformations for rendering to the rotated swap chain.
329 	// Note the rotation angle for the 2D and 3D transforms are different.
330 	// This is due to the difference in coordinate spaces.  Additionally,
331 	// the 3D matrix is specified explicitly to avoid rounding errors.
332 
333 	switch (displayRotation)
334 	{
335 	case DXGI_MODE_ROTATION_IDENTITY:
336 		m_orientationTransform2D = Matrix3x2F::Identity();
337 		m_orientationTransform3D = ScreenRotation::Rotation0;
338 		break;
339 
340 	case DXGI_MODE_ROTATION_ROTATE90:
341 		m_orientationTransform2D =
342 			Matrix3x2F::Rotation(90.0f) *
343 			Matrix3x2F::Translation(m_logicalSize.Height, 0.0f);
344 		m_orientationTransform3D = ScreenRotation::Rotation270;
345 		break;
346 
347 	case DXGI_MODE_ROTATION_ROTATE180:
348 		m_orientationTransform2D =
349 			Matrix3x2F::Rotation(180.0f) *
350 			Matrix3x2F::Translation(m_logicalSize.Width, m_logicalSize.Height);
351 		m_orientationTransform3D = ScreenRotation::Rotation180;
352 		break;
353 
354 	case DXGI_MODE_ROTATION_ROTATE270:
355 		m_orientationTransform2D =
356 			Matrix3x2F::Rotation(270.0f) *
357 			Matrix3x2F::Translation(0.0f, m_logicalSize.Width);
358 		m_orientationTransform3D = ScreenRotation::Rotation90;
359 		break;
360 
361 	default:
362 		throw ref new FailureException();
363 	}
364 
365 	DX::ThrowIfFailed(
366 		m_swapChain->SetRotation(displayRotation)
367 		);
368 
369 	// Create a render target view of the swap chain back buffer.
370 	ComPtr<ID3D11Texture2D1> backBuffer;
371 	DX::ThrowIfFailed(
372 		m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer))
373 		);
374 
375 	DX::ThrowIfFailed(
376 		m_d3dDevice->CreateRenderTargetView1(
377 			backBuffer.Get(),
378 			nullptr,
379 			&m_d3dRenderTargetView
380 			)
381 		);
382 
383 	// Set the 3D rendering viewport to target the entire window.
384 	m_screenViewport = CD3D11_VIEWPORT(
385 		0.0f,
386 		0.0f,
387 		m_d3dRenderTargetSize.Width,
388 		m_d3dRenderTargetSize.Height
389 		);
390 
391 	m_d3dContext->RSSetViewports(1, &m_screenViewport);
392 
393 	// Create a Direct2D target bitmap associated with the
394 	// swap chain back buffer and set it as the current target.
395 	D2D1_BITMAP_PROPERTIES1 bitmapProperties =
396 		D2D1::BitmapProperties1(
397 			D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
398 			D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
399 			m_dpi,
400 			m_dpi
401 			);
402 
403 	ComPtr<IDXGISurface2> dxgiBackBuffer;
404 	DX::ThrowIfFailed(
405 		m_swapChain->GetBuffer(0, IID_PPV_ARGS(&dxgiBackBuffer))
406 		);
407 
408 	DX::ThrowIfFailed(
409 		m_d2dContext->CreateBitmapFromDxgiSurface(
410 			dxgiBackBuffer.Get(),
411 			&bitmapProperties,
412 			&m_d2dTargetBitmap
413 			)
414 		);
415 
416 	m_d2dContext->SetTarget(m_d2dTargetBitmap.Get());
417 	m_d2dContext->SetDpi(m_effectiveDpi, m_effectiveDpi);
418 
419 	// Grayscale text anti-aliasing is recommended for all Windows Store apps.
420 	m_d2dContext->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);
421 }
422 
423 // Determine the dimensions of the render target and whether it will be scaled down.
UpdateRenderTargetSize()424 void DX::DeviceResources::UpdateRenderTargetSize()
425 {
426 	m_effectiveDpi = m_dpi;
427 	if (Windows::System::Profile::AnalyticsInfo::VersionInfo->DeviceFamily == L"Windows.Xbox")
428 	{
429 		m_effectiveDpi = 96.0f / static_cast<float>(m_logicalSize.Height) * 1080.0f;
430 	}
431 	else
432 	{
433 		// To improve battery life on high resolution devices, render to a smaller render target
434 		// and allow the GPU to scale the output when it is presented.
435 		if (!DisplayMetrics::SupportHighResolutions && m_dpi >= DisplayMetrics::DpiThreshold)
436 		{
437 			float width = DX::ConvertDipsToPixels(m_logicalSize.Width, m_dpi);
438 			float height = DX::ConvertDipsToPixels(m_logicalSize.Height, m_dpi);
439 
440 			// When the device is in portrait orientation, height > width. Compare the
441 			// larger dimension against the width threshold and the smaller dimension
442 			// against the height threshold.
443 			if (std::max(width, height) > DisplayMetrics::WidthThreshold && std::min(width, height) > DisplayMetrics::HeightThreshold)
444 			{
445 				// To scale the app we change the effective DPI. Logical size does not change.
446 				m_effectiveDpi /= 2.0f;
447 			}
448 		}
449 	}
450 	// Calculate the necessary render target size in pixels.
451 	m_outputSize.Width = DX::ConvertDipsToPixels(m_logicalSize.Width, m_effectiveDpi);
452 	m_outputSize.Height = DX::ConvertDipsToPixels(m_logicalSize.Height, m_effectiveDpi);
453 
454 	// Prevent zero size DirectX content from being created.
455 	m_outputSize.Width = std::max(m_outputSize.Width, 1.0f);
456 	m_outputSize.Height = std::max(m_outputSize.Height, 1.0f);
457 }
458 
459 // This method is called when the CoreWindow is created (or re-created).
460 void DX::DeviceResources::SetWindow(CoreWindow^ window)
461 {
462 	DisplayInformation^ currentDisplayInformation = DisplayInformation::GetForCurrentView();
463 
464 	m_window = window;
465 	if (Windows::System::Profile::AnalyticsInfo::VersionInfo->DeviceFamily == L"Windows.Xbox")
466 	{
467 		const auto hdi = Windows::Graphics::Display::Core::HdmiDisplayInformation::GetForCurrentView();
468 		if (hdi)
469 		{
470 			try
471 			{
472 				const auto dm = hdi->GetCurrentDisplayMode();
473 				const unsigned int hdmi_width = dm->ResolutionWidthInRawPixels;
474 				const unsigned int hdmi_height = dm->ResolutionHeightInRawPixels;
475 				// If we're running on Xbox, use the HDMI mode instead of the CoreWindow size.
476 				// In UWP, the CoreWindow is always 1920x1080, even when running at 4K.
477 
478 				m_logicalSize = Windows::Foundation::Size(hdmi_width, hdmi_height);
479 				m_dpi = currentDisplayInformation->LogicalDpi * 1.5;
480 			}
481 			catch (const Platform::Exception^)
482 			{
483 				m_logicalSize = Windows::Foundation::Size(window->Bounds.Width, window->Bounds.Height);
484 				m_dpi = currentDisplayInformation->LogicalDpi;
485 			}
486 		}
487 	}
488 	else
489 	{
490 		m_logicalSize = Windows::Foundation::Size(window->Bounds.Width, window->Bounds.Height);
491 		m_dpi = currentDisplayInformation->LogicalDpi;
492 	}
493 	m_nativeOrientation = currentDisplayInformation->NativeOrientation;
494 	m_currentOrientation = currentDisplayInformation->CurrentOrientation;
495 
496 	m_d2dContext->SetDpi(m_dpi, m_dpi);
497 
498 	CreateWindowSizeDependentResources();
499 }
500 
501 // This method is called in the event handler for the SizeChanged event.
SetLogicalSize(Windows::Foundation::Size logicalSize)502 void DX::DeviceResources::SetLogicalSize(Windows::Foundation::Size logicalSize)
503 {
504 	if (m_logicalSize != logicalSize)
505 	{
506 		m_logicalSize = logicalSize;
507 		CreateWindowSizeDependentResources();
508 	}
509 }
510 
511 // This method is called in the event handler for the DpiChanged event.
SetDpi(float dpi)512 void DX::DeviceResources::SetDpi(float dpi)
513 {
514 	if (dpi != m_dpi)
515 	{
516 		m_dpi = dpi;
517 
518 		// When the display DPI changes, the logical size of the window (measured in Dips) also changes and needs to be updated.
519 		m_logicalSize = Windows::Foundation::Size(m_window->Bounds.Width, m_window->Bounds.Height);
520 
521 		m_d2dContext->SetDpi(m_dpi, m_dpi);
522 		CreateWindowSizeDependentResources();
523 	}
524 }
525 
526 // This method is called in the event handler for the OrientationChanged event.
SetCurrentOrientation(DisplayOrientations currentOrientation)527 void DX::DeviceResources::SetCurrentOrientation(DisplayOrientations currentOrientation)
528 {
529 	if (m_currentOrientation != currentOrientation)
530 	{
531 		m_currentOrientation = currentOrientation;
532 		CreateWindowSizeDependentResources();
533 	}
534 }
535 
536 // This method is called in the event handler for the DisplayContentsInvalidated event.
ValidateDevice()537 void DX::DeviceResources::ValidateDevice()
538 {
539 	// The D3D Device is no longer valid if the default adapter changed since the device
540 	// was created or if the device has been removed.
541 
542 	// First, get the information for the default adapter from when the device was created.
543 
544 	ComPtr<IDXGIDevice3> dxgiDevice;
545 	DX::ThrowIfFailed(m_d3dDevice.As(&dxgiDevice));
546 
547 	ComPtr<IDXGIAdapter> deviceAdapter;
548 	DX::ThrowIfFailed(dxgiDevice->GetAdapter(&deviceAdapter));
549 
550 	ComPtr<IDXGIFactory4> deviceFactory;
551 	DX::ThrowIfFailed(deviceAdapter->GetParent(IID_PPV_ARGS(&deviceFactory)));
552 
553 	ComPtr<IDXGIAdapter1> previousDefaultAdapter;
554 	DX::ThrowIfFailed(deviceFactory->EnumAdapters1(0, &previousDefaultAdapter));
555 
556 	DXGI_ADAPTER_DESC1 previousDesc;
557 	DX::ThrowIfFailed(previousDefaultAdapter->GetDesc1(&previousDesc));
558 
559 	// Next, get the information for the current default adapter.
560 
561 	ComPtr<IDXGIFactory4> currentFactory;
562 	DX::ThrowIfFailed(CreateDXGIFactory1(IID_PPV_ARGS(&currentFactory)));
563 
564 	ComPtr<IDXGIAdapter1> currentDefaultAdapter;
565 	DX::ThrowIfFailed(currentFactory->EnumAdapters1(0, &currentDefaultAdapter));
566 
567 	DXGI_ADAPTER_DESC1 currentDesc;
568 	DX::ThrowIfFailed(currentDefaultAdapter->GetDesc1(&currentDesc));
569 
570 	// If the adapter LUIDs don't match, or if the device reports that it has been removed,
571 	// a new D3D device must be created.
572 
573 	if (previousDesc.AdapterLuid.LowPart != currentDesc.AdapterLuid.LowPart ||
574 		previousDesc.AdapterLuid.HighPart != currentDesc.AdapterLuid.HighPart ||
575 		FAILED(m_d3dDevice->GetDeviceRemovedReason()))
576 	{
577 		// Release references to resources related to the old device.
578 		dxgiDevice = nullptr;
579 		deviceAdapter = nullptr;
580 		deviceFactory = nullptr;
581 		previousDefaultAdapter = nullptr;
582 
583 		// Create a new device and swap chain.
584 		HandleDeviceLost();
585 	}
586 }
587 
588 // Recreate all device resources and set them back to the current state.
HandleDeviceLost()589 void DX::DeviceResources::HandleDeviceLost()
590 {
591 	m_swapChain = nullptr;
592 
593 	if (m_deviceNotify != nullptr)
594 	{
595 		m_deviceNotify->OnDeviceLost();
596 	}
597 
598 	CreateDeviceResources();
599 	m_d2dContext->SetDpi(m_dpi, m_dpi);
600 	CreateWindowSizeDependentResources();
601 
602 	if (m_deviceNotify != nullptr)
603 	{
604 		m_deviceNotify->OnDeviceRestored();
605 	}
606 }
607 
608 // Register our DeviceNotify to be informed on device lost and creation.
RegisterDeviceNotify(DX::IDeviceNotify * deviceNotify)609 void DX::DeviceResources::RegisterDeviceNotify(DX::IDeviceNotify* deviceNotify)
610 {
611 	m_deviceNotify = deviceNotify;
612 }
613 
614 // Call this method when the app suspends. It provides a hint to the driver that the app
615 // is entering an idle state and that temporary buffers can be reclaimed for use by other apps.
Trim()616 void DX::DeviceResources::Trim()
617 {
618 	ComPtr<IDXGIDevice3> dxgiDevice;
619 	m_d3dDevice.As(&dxgiDevice);
620 
621 	dxgiDevice->Trim();
622 }
623 
624 // Present the contents of the swap chain to the screen.
Present()625 void DX::DeviceResources::Present()
626 {
627 	// The first argument instructs DXGI to block until VSync, putting the application
628 	// to sleep until the next VSync. This ensures we don't waste any cycles rendering
629 	// frames that will never be displayed to the screen.
630 	DXGI_PRESENT_PARAMETERS parameters = { 0 };
631 	HRESULT hr = m_swapChain->Present1(1, 0, &parameters);
632 
633 	// Discard the contents of the render target.
634 	// This is a valid operation only when the existing contents will be entirely
635 	// overwritten. If dirty or scroll rects are used, this call should be removed.
636 	m_d3dContext->DiscardView1(m_d3dRenderTargetView.Get(), nullptr, 0);
637 
638 	// If the device was removed either by a disconnection or a driver upgrade, we
639 	// must recreate all device resources.
640 	if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
641 	{
642 		HandleDeviceLost();
643 	}
644 	else
645 	{
646 		DX::ThrowIfFailed(hr);
647 	}
648 }
649 
650 // This method determines the rotation between the display device's native orientation and the
651 // current display orientation.
ComputeDisplayRotation()652 DXGI_MODE_ROTATION DX::DeviceResources::ComputeDisplayRotation()
653 {
654 	DXGI_MODE_ROTATION rotation = DXGI_MODE_ROTATION_UNSPECIFIED;
655 
656 	// Note: NativeOrientation can only be Landscape or Portrait even though
657 	// the DisplayOrientations enum has other values.
658 	switch (m_nativeOrientation)
659 	{
660 	case DisplayOrientations::Landscape:
661 		switch (m_currentOrientation)
662 		{
663 		case DisplayOrientations::Landscape:
664 			rotation = DXGI_MODE_ROTATION_IDENTITY;
665 			break;
666 
667 		case DisplayOrientations::Portrait:
668 			rotation = DXGI_MODE_ROTATION_ROTATE270;
669 			break;
670 
671 		case DisplayOrientations::LandscapeFlipped:
672 			rotation = DXGI_MODE_ROTATION_ROTATE180;
673 			break;
674 
675 		case DisplayOrientations::PortraitFlipped:
676 			rotation = DXGI_MODE_ROTATION_ROTATE90;
677 			break;
678 		}
679 		break;
680 
681 	case DisplayOrientations::Portrait:
682 		switch (m_currentOrientation)
683 		{
684 		case DisplayOrientations::Landscape:
685 			rotation = DXGI_MODE_ROTATION_ROTATE90;
686 			break;
687 
688 		case DisplayOrientations::Portrait:
689 			rotation = DXGI_MODE_ROTATION_IDENTITY;
690 			break;
691 
692 		case DisplayOrientations::LandscapeFlipped:
693 			rotation = DXGI_MODE_ROTATION_ROTATE270;
694 			break;
695 
696 		case DisplayOrientations::PortraitFlipped:
697 			rotation = DXGI_MODE_ROTATION_ROTATE180;
698 			break;
699 		}
700 		break;
701 	}
702 	return rotation;
703 }
704