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