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(¤tFactory)));
563
564 ComPtr<IDXGIAdapter1> currentDefaultAdapter;
565 DX::ThrowIfFailed(currentFactory->EnumAdapters1(0, ¤tDefaultAdapter));
566
567 DXGI_ADAPTER_DESC1 currentDesc;
568 DX::ThrowIfFailed(currentDefaultAdapter->GetDesc1(¤tDesc));
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, ¶meters);
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