1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "DeviceManagerDx.h"
7 #include "D3D11Checks.h"
8 #include "gfxConfig.h"
9 #include "GfxDriverInfo.h"
10 #include "gfxPrefs.h"
11 #include "gfxWindowsPlatform.h"
12 #include "mozilla/D3DMessageUtils.h"
13 #include "mozilla/Telemetry.h"
14 #include "mozilla/WindowsVersion.h"
15 #include "mozilla/gfx/GraphicsMessages.h"
16 #include "mozilla/gfx/Logging.h"
17 #include "mozilla/layers/CompositorThread.h"
18 #include "nsIGfxInfo.h"
19 #include <d3d11.h>
20 #include <ddraw.h>
21 
22 namespace mozilla {
23 namespace gfx {
24 
25 using namespace mozilla::widget;
26 
27 StaticAutoPtr<DeviceManagerDx> DeviceManagerDx::sInstance;
28 
29 // We don't have access to the D3D11CreateDevice type in gfxWindowsPlatform.h,
30 // since it doesn't include d3d11.h, so we use a static here. It should only
31 // be used within InitializeD3D11.
32 decltype(D3D11CreateDevice)* sD3D11CreateDeviceFn = nullptr;
33 
34 // We don't have access to the DirectDrawCreateEx type in gfxWindowsPlatform.h,
35 // since it doesn't include ddraw.h, so we use a static here. It should only
36 // be used within InitializeDirectDrawConfig.
37 decltype(DirectDrawCreateEx)* sDirectDrawCreateExFn = nullptr;
38 
39 /* static */ void
Init()40 DeviceManagerDx::Init()
41 {
42   sInstance = new DeviceManagerDx();
43 }
44 
45 /* static */ void
Shutdown()46 DeviceManagerDx::Shutdown()
47 {
48   sInstance = nullptr;
49 }
50 
DeviceManagerDx()51 DeviceManagerDx::DeviceManagerDx()
52  : mDeviceLock("gfxWindowsPlatform.mDeviceLock"),
53    mCompositorDeviceSupportsVideo(false)
54 {
55   // Set up the D3D11 feature levels we can ask for.
56   if (IsWin8OrLater()) {
57     mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_11_1);
58   }
59   mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_11_0);
60   mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_10_1);
61   mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_10_0);
62 }
63 
64 bool
LoadD3D11()65 DeviceManagerDx::LoadD3D11()
66 {
67   FeatureState& d3d11 = gfxConfig::GetFeature(Feature::D3D11_COMPOSITING);
68   MOZ_ASSERT(d3d11.IsEnabled());
69 
70   if (sD3D11CreateDeviceFn) {
71     return true;
72   }
73 
74   nsModuleHandle module(LoadLibrarySystem32(L"d3d11.dll"));
75   if (!module) {
76     d3d11.SetFailed(FeatureStatus::Unavailable, "Direct3D11 not available on this computer",
77                     NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_LIB"));
78     return false;
79   }
80 
81   sD3D11CreateDeviceFn =
82     (decltype(D3D11CreateDevice)*)GetProcAddress(module, "D3D11CreateDevice");
83   if (!sD3D11CreateDeviceFn) {
84     // We should just be on Windows Vista or XP in this case.
85     d3d11.SetFailed(FeatureStatus::Unavailable, "Direct3D11 not available on this computer",
86                     NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_FUNCPTR"));
87     return false;
88   }
89 
90   mD3D11Module.steal(module);
91   return true;
92 }
93 
94 void
ReleaseD3D11()95 DeviceManagerDx::ReleaseD3D11()
96 {
97   MOZ_ASSERT(!mCompositorDevice);
98   MOZ_ASSERT(!mContentDevice);
99   MOZ_ASSERT(!mDecoderDevice);
100 
101   mD3D11Module.reset();
102   sD3D11CreateDeviceFn = nullptr;
103 }
104 
105 static inline bool
ProcessOwnsCompositor()106 ProcessOwnsCompositor()
107 {
108   return XRE_GetProcessType() == GeckoProcessType_GPU ||
109          (XRE_IsParentProcess() && !gfxConfig::IsEnabled(Feature::GPU_PROCESS));
110 }
111 
112 bool
CreateCompositorDevices()113 DeviceManagerDx::CreateCompositorDevices()
114 {
115   MOZ_ASSERT(ProcessOwnsCompositor());
116 
117   FeatureState& d3d11 = gfxConfig::GetFeature(Feature::D3D11_COMPOSITING);
118   MOZ_ASSERT(d3d11.IsEnabled());
119 
120   if (!LoadD3D11()) {
121     return false;
122   }
123 
124   CreateCompositorDevice(d3d11);
125 
126   if (!d3d11.IsEnabled()) {
127     MOZ_ASSERT(!mCompositorDevice);
128     ReleaseD3D11();
129     return false;
130   }
131 
132   // We leak these everywhere and we need them our entire runtime anyway, let's
133   // leak it here as well. We keep the pointer to sD3D11CreateDeviceFn around
134   // as well for D2D1 and device resets.
135   mD3D11Module.disown();
136 
137   MOZ_ASSERT(mCompositorDevice);
138   return d3d11.IsEnabled();
139 }
140 
141 void
ImportDeviceInfo(const D3D11DeviceStatus & aDeviceStatus)142 DeviceManagerDx::ImportDeviceInfo(const D3D11DeviceStatus& aDeviceStatus)
143 {
144   MOZ_ASSERT(!ProcessOwnsCompositor());
145 
146   mDeviceStatus = Some(aDeviceStatus);
147 }
148 
149 void
ExportDeviceInfo(D3D11DeviceStatus * aOut)150 DeviceManagerDx::ExportDeviceInfo(D3D11DeviceStatus* aOut)
151 {
152   // Even though the parent process might not own the compositor, we still
153   // populate DeviceManagerDx with device statistics (for simplicity).
154   // That means it still gets queried for compositor information.
155   MOZ_ASSERT(XRE_IsParentProcess() || XRE_GetProcessType() == GeckoProcessType_GPU);
156 
157   if (mDeviceStatus) {
158     *aOut = mDeviceStatus.value();
159   }
160 }
161 
162 void
CreateContentDevices()163 DeviceManagerDx::CreateContentDevices()
164 {
165   MOZ_ASSERT(gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING));
166 
167   if (!LoadD3D11()) {
168     return;
169   }
170 
171   // We should have been assigned a DeviceStatus from the parent process,
172   // GPU process, or the same process if using in-process compositing.
173   MOZ_ASSERT(mDeviceStatus);
174 
175   if (CreateContentDevice() == FeatureStatus::CrashedInHandler) {
176     DisableD3D11AfterCrash();
177   }
178 }
179 
180 IDXGIAdapter1*
GetDXGIAdapter()181 DeviceManagerDx::GetDXGIAdapter()
182 {
183   if (mAdapter) {
184     return mAdapter;
185   }
186 
187   nsModuleHandle dxgiModule(LoadLibrarySystem32(L"dxgi.dll"));
188   decltype(CreateDXGIFactory1)* createDXGIFactory1 = (decltype(CreateDXGIFactory1)*)
189     GetProcAddress(dxgiModule, "CreateDXGIFactory1");
190 
191   if (!createDXGIFactory1) {
192     return nullptr;
193   }
194 
195   // Try to use a DXGI 1.1 adapter in order to share resources
196   // across processes.
197   RefPtr<IDXGIFactory1> factory1;
198   HRESULT hr = createDXGIFactory1(__uuidof(IDXGIFactory1),
199                                   getter_AddRefs(factory1));
200   if (FAILED(hr) || !factory1) {
201     // This seems to happen with some people running the iZ3D driver.
202     // They won't get acceleration.
203     return nullptr;
204   }
205 
206   if (!mDeviceStatus) {
207     // If we haven't created a device yet, and have no existing device status,
208     // then this must be the compositor device. Pick the first adapter we can.
209     if (FAILED(factory1->EnumAdapters1(0, getter_AddRefs(mAdapter)))) {
210       return nullptr;
211     }
212   } else {
213     // In the UI and GPU process, we clear mDeviceStatus on device reset, so we
214     // should never reach here. Furthermore, the UI process does not create
215     // devices when using a GPU process.
216     //
217     // So, this should only ever get called on the content process.
218     MOZ_ASSERT(XRE_IsContentProcess());
219 
220     // In the child process, we search for the adapter that matches the parent
221     // process. The first adapter can be mismatched on dual-GPU systems.
222     for (UINT index = 0; ; index++) {
223       RefPtr<IDXGIAdapter1> adapter;
224       if (FAILED(factory1->EnumAdapters1(index, getter_AddRefs(adapter)))) {
225         break;
226       }
227 
228       const DxgiAdapterDesc& preferred = mDeviceStatus->adapter();
229 
230       DXGI_ADAPTER_DESC desc;
231       if (SUCCEEDED(adapter->GetDesc(&desc)) &&
232           desc.AdapterLuid.HighPart == preferred.AdapterLuid.HighPart &&
233           desc.AdapterLuid.LowPart == preferred.AdapterLuid.LowPart &&
234           desc.VendorId == preferred.VendorId &&
235           desc.DeviceId == preferred.DeviceId)
236       {
237         mAdapter = adapter.forget();
238         break;
239       }
240     }
241   }
242 
243   if (!mAdapter) {
244     return nullptr;
245   }
246 
247   // We leak this module everywhere, we might as well do so here as well.
248   dxgiModule.disown();
249   return mAdapter;
250 }
251 
252 bool
CreateCompositorDeviceHelper(FeatureState & aD3d11,IDXGIAdapter1 * aAdapter,bool aAttemptVideoSupport,RefPtr<ID3D11Device> & aOutDevice)253 DeviceManagerDx::CreateCompositorDeviceHelper(
254   FeatureState& aD3d11, IDXGIAdapter1* aAdapter, bool aAttemptVideoSupport, RefPtr<ID3D11Device>& aOutDevice)
255 {
256   // Check if a failure was injected for testing.
257   if (gfxPrefs::DeviceFailForTesting()) {
258     aD3d11.SetFailed(FeatureStatus::Failed, "Direct3D11 device failure simulated by preference",
259                      NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_SIM"));
260     return false;
261   }
262 
263   // Use D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS
264   // to prevent bug 1092260. IE 11 also uses this flag.
265   UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS;
266   if (aAttemptVideoSupport) {
267     flags |= D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
268   }
269 
270   HRESULT hr;
271   RefPtr<ID3D11Device> device;
272   if (!CreateDevice(aAdapter, D3D_DRIVER_TYPE_UNKNOWN, flags, hr, device)) {
273     if (!aAttemptVideoSupport) {
274       gfxCriticalError() << "Crash during D3D11 device creation";
275       aD3d11.SetFailed(FeatureStatus::CrashedInHandler, "Crashed trying to acquire a D3D11 device",
276                        NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_DEVICE1"));
277     }
278     return false;
279   }
280 
281   if (FAILED(hr) || !device) {
282     if (!aAttemptVideoSupport) {
283       aD3d11.SetFailed(FeatureStatus::Failed, "Failed to acquire a D3D11 device",
284                        NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_DEVICE2"));
285     }
286     return false;
287   }
288   if (!D3D11Checks::DoesDeviceWork()) {
289     if (!aAttemptVideoSupport) {
290       aD3d11.SetFailed(FeatureStatus::Broken, "Direct3D11 device was determined to be broken",
291                        NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_BROKEN"));
292     }
293     return false;
294   }
295 
296   aOutDevice = device;
297   return true;
298 }
299 
300 void
CreateCompositorDevice(FeatureState & d3d11)301 DeviceManagerDx::CreateCompositorDevice(FeatureState& d3d11)
302 {
303   if (gfxPrefs::LayersD3D11ForceWARP()) {
304     CreateWARPCompositorDevice();
305     return;
306   }
307 
308   RefPtr<IDXGIAdapter1> adapter = GetDXGIAdapter();
309   if (!adapter) {
310     d3d11.SetFailed(FeatureStatus::Unavailable, "Failed to acquire a DXGI adapter",
311                     NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_DXGI"));
312     return;
313   }
314 
315   if (XRE_IsGPUProcess() && !D3D11Checks::DoesRemotePresentWork(adapter)) {
316     d3d11.SetFailed(
317       FeatureStatus::Unavailable,
318       "DXGI does not support out-of-process presentation",
319       NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_REMOTE_PRESENT"));
320     return;
321   }
322 
323   RefPtr<ID3D11Device> device;
324   if (!CreateCompositorDeviceHelper(d3d11, adapter, true, device)) {
325     // Try again without video support and record that it failed.
326     mCompositorDeviceSupportsVideo = false;
327     if (!CreateCompositorDeviceHelper(d3d11, adapter, false, device)) {
328       return;
329     }
330   } else {
331     mCompositorDeviceSupportsVideo = true;
332   }
333 
334   // Only test this when not using WARP since it can fail and cause
335   // GetDeviceRemovedReason to return weird values.
336   bool textureSharingWorks = D3D11Checks::DoesTextureSharingWork(device);
337 
338   DXGI_ADAPTER_DESC desc;
339   PodZero(&desc);
340   adapter->GetDesc(&desc);
341 
342   if (!textureSharingWorks) {
343     gfxConfig::SetFailed(Feature::D3D11_HW_ANGLE,
344                          FeatureStatus::Broken,
345                          "Texture sharing doesn't work");
346   }
347   if (D3D11Checks::DoesRenderTargetViewNeedRecreating(device)) {
348     gfxConfig::SetFailed(Feature::D3D11_HW_ANGLE,
349                          FeatureStatus::Broken,
350                          "RenderTargetViews need recreating");
351   }
352   if (XRE_IsParentProcess()) {
353     // It seems like this may only happen when we're using the NVIDIA gpu
354     D3D11Checks::WarnOnAdapterMismatch(device);
355   }
356 
357   int featureLevel = device->GetFeatureLevel();
358   {
359     MutexAutoLock lock(mDeviceLock);
360     mCompositorDevice = device;
361     mDeviceStatus = Some(D3D11DeviceStatus(
362       false,
363       textureSharingWorks,
364       featureLevel,
365       DxgiAdapterDesc::From(desc)));
366   }
367   mCompositorDevice->SetExceptionMode(0);
368 }
369 
370 bool
CreateDevice(IDXGIAdapter * aAdapter,D3D_DRIVER_TYPE aDriverType,UINT aFlags,HRESULT & aResOut,RefPtr<ID3D11Device> & aOutDevice)371 DeviceManagerDx::CreateDevice(IDXGIAdapter* aAdapter,
372                                  D3D_DRIVER_TYPE aDriverType,
373                                  UINT aFlags,
374                                  HRESULT& aResOut,
375                                  RefPtr<ID3D11Device>& aOutDevice)
376 {
377   MOZ_SEH_TRY {
378     aResOut = sD3D11CreateDeviceFn(
379       aAdapter, aDriverType, nullptr,
380       aFlags,
381       mFeatureLevels.Elements(), mFeatureLevels.Length(),
382       D3D11_SDK_VERSION, getter_AddRefs(aOutDevice), nullptr, nullptr);
383   } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
384     return false;
385   }
386   return true;
387 }
388 
389 void
CreateWARPCompositorDevice()390 DeviceManagerDx::CreateWARPCompositorDevice()
391 {
392   ScopedGfxFeatureReporter reporterWARP("D3D11-WARP", gfxPrefs::LayersD3D11ForceWARP());
393   FeatureState& d3d11 = gfxConfig::GetFeature(Feature::D3D11_COMPOSITING);
394 
395   HRESULT hr;
396   RefPtr<ID3D11Device> device;
397 
398   // Use D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS
399   // to prevent bug 1092260. IE 11 also uses this flag.
400   UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
401   if (!CreateDevice(nullptr, D3D_DRIVER_TYPE_WARP, flags, hr, device)) {
402     gfxCriticalError() << "Exception occurred initializing WARP D3D11 device!";
403     d3d11.SetFailed(FeatureStatus::CrashedInHandler, "Crashed creating a D3D11 WARP device",
404                      NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_WARP_DEVICE"));
405   }
406 
407   if (FAILED(hr) || !device) {
408     // This should always succeed... in theory.
409     gfxCriticalError() << "Failed to initialize WARP D3D11 device! " << hexa(hr);
410     d3d11.SetFailed(FeatureStatus::Failed, "Failed to create a D3D11 WARP device",
411                     NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_WARP_DEVICE2"));
412     return;
413   }
414 
415   // Only test for texture sharing on Windows 8 since it puts the device into
416   // an unusable state if used on Windows 7
417   bool textureSharingWorks = false;
418   if (IsWin8OrLater()) {
419     textureSharingWorks = D3D11Checks::DoesTextureSharingWork(device);
420   }
421 
422   DxgiAdapterDesc nullAdapter;
423   PodZero(&nullAdapter);
424 
425   int featureLevel = device->GetFeatureLevel();
426   {
427     MutexAutoLock lock(mDeviceLock);
428     mCompositorDevice = device;
429     mDeviceStatus = Some(D3D11DeviceStatus(
430       true,
431       textureSharingWorks,
432       featureLevel,
433       nullAdapter));
434   }
435   mCompositorDevice->SetExceptionMode(0);
436 
437   reporterWARP.SetSuccessful();
438 }
439 
440 FeatureStatus
CreateContentDevice()441 DeviceManagerDx::CreateContentDevice()
442 {
443   RefPtr<IDXGIAdapter1> adapter;
444   if (!mDeviceStatus->isWARP()) {
445     adapter = GetDXGIAdapter();
446     if (!adapter) {
447       gfxCriticalNote << "Could not get a DXGI adapter";
448       return FeatureStatus::Unavailable;
449     }
450   }
451 
452   HRESULT hr;
453   RefPtr<ID3D11Device> device;
454 
455   UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
456   D3D_DRIVER_TYPE type = mDeviceStatus->isWARP()
457                          ? D3D_DRIVER_TYPE_WARP
458                          : D3D_DRIVER_TYPE_UNKNOWN;
459   if (!CreateDevice(adapter, type, flags, hr, device)) {
460     gfxCriticalNote << "Recovered from crash while creating a D3D11 content device";
461     gfxWindowsPlatform::RecordContentDeviceFailure(TelemetryDeviceCode::Content);
462     return FeatureStatus::CrashedInHandler;
463   }
464 
465   if (FAILED(hr) || !device) {
466     gfxCriticalNote << "Failed to create a D3D11 content device: " << hexa(hr);
467     gfxWindowsPlatform::RecordContentDeviceFailure(TelemetryDeviceCode::Content);
468     return FeatureStatus::Failed;
469   }
470 
471   // InitializeD2D() will abort early if the compositor device did not support
472   // texture sharing. If we're in the content process, we can't rely on the
473   // parent device alone: some systems have dual GPUs that are capable of
474   // binding the parent and child processes to different GPUs. As a safety net,
475   // we re-check texture sharing against the newly created D3D11 content device.
476   // If it fails, we won't use Direct2D.
477   if (XRE_IsContentProcess()) {
478     if (!D3D11Checks::DoesTextureSharingWork(device)) {
479       return FeatureStatus::Failed;
480     }
481 
482     DebugOnly<bool> ok = ContentAdapterIsParentAdapter(device);
483     MOZ_ASSERT(ok);
484   }
485 
486   {
487     MutexAutoLock lock(mDeviceLock);
488     mContentDevice = device;
489   }
490   mContentDevice->SetExceptionMode(0);
491 
492   RefPtr<ID3D10Multithread> multi;
493   hr = mContentDevice->QueryInterface(__uuidof(ID3D10Multithread), getter_AddRefs(multi));
494   if (SUCCEEDED(hr) && multi) {
495     multi->SetMultithreadProtected(TRUE);
496   }
497   return FeatureStatus::Available;
498 }
499 
500 RefPtr<ID3D11Device>
CreateDecoderDevice()501 DeviceManagerDx::CreateDecoderDevice()
502 {
503   bool isAMD = false;
504   {
505     MutexAutoLock lock(mDeviceLock);
506     if (!mDeviceStatus) {
507       return nullptr;
508     }
509     isAMD = mDeviceStatus->adapter().VendorId == 0x1002;
510   }
511 
512   bool reuseDevice = false;
513   if (gfxPrefs::Direct3D11ReuseDecoderDevice() < 0) {
514     // Use the default logic, which is to allow reuse of devices on AMD, but create
515     // separate devices everywhere else.
516     if (isAMD) {
517       reuseDevice = true;
518     }
519   } else if (gfxPrefs::Direct3D11ReuseDecoderDevice() > 0) {
520     reuseDevice = true;
521   }
522 
523   if (reuseDevice) {
524     if (mCompositorDevice && mCompositorDeviceSupportsVideo && !mDecoderDevice) {
525       mDecoderDevice = mCompositorDevice;
526 
527       RefPtr<ID3D10Multithread> multi;
528       mDecoderDevice->QueryInterface(__uuidof(ID3D10Multithread), getter_AddRefs(multi));
529       if (multi) {
530         multi->SetMultithreadProtected(TRUE);
531       }
532     }
533 
534     if (mDecoderDevice) {
535       RefPtr<ID3D11Device> dev = mDecoderDevice;
536       return dev.forget();
537     }
538   }
539 
540    if (!sD3D11CreateDeviceFn) {
541     // We should just be on Windows Vista or XP in this case.
542     return nullptr;
543   }
544 
545   RefPtr<IDXGIAdapter1> adapter = GetDXGIAdapter();
546   if (!adapter) {
547     return nullptr;
548   }
549 
550   HRESULT hr;
551   RefPtr<ID3D11Device> device;
552 
553   UINT flags = D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS |
554                D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
555   if (!CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, flags, hr, device)) {
556     return nullptr;
557   }
558   if (FAILED(hr) || !device || !D3D11Checks::DoesDeviceWork()) {
559     return nullptr;
560   }
561 
562   RefPtr<ID3D10Multithread> multi;
563   device->QueryInterface(__uuidof(ID3D10Multithread), getter_AddRefs(multi));
564 
565   multi->SetMultithreadProtected(TRUE);
566 
567   if (reuseDevice) {
568     mDecoderDevice = device;
569   }
570   return device;
571 }
572 
573 void
ResetDevices()574 DeviceManagerDx::ResetDevices()
575 {
576   MutexAutoLock lock(mDeviceLock);
577 
578   mAdapter = nullptr;
579   mCompositorDevice = nullptr;
580   mContentDevice = nullptr;
581   mDeviceStatus = Nothing();
582   mDeviceResetReason = Nothing();
583   Factory::SetDirect3D11Device(nullptr);
584 }
585 
586 bool
MaybeResetAndReacquireDevices()587 DeviceManagerDx::MaybeResetAndReacquireDevices()
588 {
589   DeviceResetReason resetReason;
590   if (!HasDeviceReset(&resetReason)) {
591     return false;
592   }
593 
594   if (resetReason != DeviceResetReason::FORCED_RESET) {
595     Telemetry::Accumulate(Telemetry::DEVICE_RESET_REASON, uint32_t(resetReason));
596   }
597 
598   bool createCompositorDevice = !!mCompositorDevice;
599   bool createContentDevice = !!mContentDevice;
600 
601   ResetDevices();
602 
603   if (createCompositorDevice && !CreateCompositorDevices()) {
604     // Just stop, don't try anything more
605     return true;
606   }
607   if (createContentDevice) {
608     CreateContentDevices();
609   }
610 
611   return true;
612 }
613 
614 bool
ContentAdapterIsParentAdapter(ID3D11Device * device)615 DeviceManagerDx::ContentAdapterIsParentAdapter(ID3D11Device* device)
616 {
617   DXGI_ADAPTER_DESC desc;
618   if (!D3D11Checks::GetDxgiDesc(device, &desc)) {
619     gfxCriticalNote << "Could not query device DXGI adapter info";
620     return false;
621   }
622 
623   const DxgiAdapterDesc& preferred = mDeviceStatus->adapter();
624 
625   if (desc.VendorId != preferred.VendorId ||
626       desc.DeviceId != preferred.DeviceId ||
627       desc.SubSysId != preferred.SubSysId ||
628       desc.AdapterLuid.HighPart != preferred.AdapterLuid.HighPart ||
629       desc.AdapterLuid.LowPart != preferred.AdapterLuid.LowPart)
630   {
631     gfxCriticalNote <<
632       "VendorIDMismatch P " <<
633       hexa(preferred.VendorId) << " " <<
634       hexa(desc.VendorId);
635     return false;
636   }
637 
638   return true;
639 }
640 
HResultToResetReason(HRESULT hr)641 static DeviceResetReason HResultToResetReason(HRESULT hr)
642 {
643   switch (hr) {
644   case DXGI_ERROR_DEVICE_HUNG:
645     return DeviceResetReason::HUNG;
646   case DXGI_ERROR_DEVICE_REMOVED:
647     return DeviceResetReason::REMOVED;
648   case DXGI_ERROR_DEVICE_RESET:
649     return DeviceResetReason::RESET;
650   case DXGI_ERROR_DRIVER_INTERNAL_ERROR:
651     return DeviceResetReason::DRIVER_ERROR;
652   case DXGI_ERROR_INVALID_CALL:
653     return DeviceResetReason::INVALID_CALL;
654   case E_OUTOFMEMORY:
655     return DeviceResetReason::OUT_OF_MEMORY;
656   default:
657     MOZ_ASSERT(false);
658   }
659   return DeviceResetReason::UNKNOWN;
660 }
661 
662 bool
HasDeviceReset(DeviceResetReason * aOutReason)663 DeviceManagerDx::HasDeviceReset(DeviceResetReason* aOutReason)
664 {
665   MutexAutoLock lock(mDeviceLock);
666 
667   if (mDeviceResetReason) {
668     if (aOutReason) {
669       *aOutReason = mDeviceResetReason.value();
670     }
671     return true;
672   }
673 
674   DeviceResetReason reason;
675   if (GetAnyDeviceRemovedReason(&reason)) {
676     mDeviceResetReason = Some(reason);
677     if (aOutReason) {
678       *aOutReason = reason;
679     }
680     return true;
681   }
682 
683   return false;
684 }
685 
686 static inline bool
DidDeviceReset(const RefPtr<ID3D11Device> & aDevice,DeviceResetReason * aOutReason)687 DidDeviceReset(const RefPtr<ID3D11Device>& aDevice, DeviceResetReason* aOutReason)
688 {
689   if (!aDevice) {
690     return false;
691   }
692   HRESULT hr = aDevice->GetDeviceRemovedReason();
693   if (hr == S_OK) {
694     return false;
695   }
696 
697   *aOutReason = HResultToResetReason(hr);
698   return true;
699 }
700 
701 bool
GetAnyDeviceRemovedReason(DeviceResetReason * aOutReason)702 DeviceManagerDx::GetAnyDeviceRemovedReason(DeviceResetReason* aOutReason)
703 {
704   // Caller must own the lock, since we access devices directly, and can be
705   // called from any thread.
706   mDeviceLock.AssertCurrentThreadOwns();
707 
708   if (DidDeviceReset(mCompositorDevice, aOutReason) ||
709       DidDeviceReset(mContentDevice, aOutReason))
710   {
711     return true;
712   }
713 
714   if (XRE_IsParentProcess() &&
715       NS_IsMainThread() &&
716       gfxPrefs::DeviceResetForTesting())
717   {
718     gfxPrefs::SetDeviceResetForTesting(0);
719     *aOutReason = DeviceResetReason::FORCED_RESET;
720     return true;
721   }
722 
723   return false;
724 }
725 
726 void
ForceDeviceReset(ForcedDeviceResetReason aReason)727 DeviceManagerDx::ForceDeviceReset(ForcedDeviceResetReason aReason)
728 {
729   Telemetry::Accumulate(Telemetry::FORCED_DEVICE_RESET_REASON, uint32_t(aReason));
730   {
731     MutexAutoLock lock(mDeviceLock);
732     mDeviceResetReason = Some(DeviceResetReason::FORCED_RESET);
733   }
734 }
735 
736 void
NotifyD3D9DeviceReset()737 DeviceManagerDx::NotifyD3D9DeviceReset()
738 {
739   MutexAutoLock lock(mDeviceLock);
740   mDeviceResetReason = Some(DeviceResetReason::D3D9_RESET);
741 }
742 
743 void
DisableD3D11AfterCrash()744 DeviceManagerDx::DisableD3D11AfterCrash()
745 {
746   gfxConfig::Disable(Feature::D3D11_COMPOSITING,
747     FeatureStatus::CrashedInHandler,
748     "Crashed while acquiring a Direct3D11 device",
749     NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_CRASH"));
750   ResetDevices();
751 }
752 
753 RefPtr<ID3D11Device>
GetCompositorDevice()754 DeviceManagerDx::GetCompositorDevice()
755 {
756   MutexAutoLock lock(mDeviceLock);
757   return mCompositorDevice;
758 }
759 
760 RefPtr<ID3D11Device>
GetContentDevice()761 DeviceManagerDx::GetContentDevice()
762 {
763   MutexAutoLock lock(mDeviceLock);
764   return mContentDevice;
765 }
766 
767 unsigned
GetCompositorFeatureLevel() const768 DeviceManagerDx::GetCompositorFeatureLevel() const
769 {
770   if (!mDeviceStatus) {
771     return 0;
772   }
773   return mDeviceStatus->featureLevel();
774 }
775 
776 bool
TextureSharingWorks()777 DeviceManagerDx::TextureSharingWorks()
778 {
779   MutexAutoLock lock(mDeviceLock);
780   if (!mDeviceStatus) {
781     return false;
782   }
783   return mDeviceStatus->textureSharingWorks();
784 }
785 
786 bool
CanInitializeKeyedMutexTextures()787 DeviceManagerDx::CanInitializeKeyedMutexTextures()
788 {
789   MutexAutoLock lock(mDeviceLock);
790   if (!mDeviceStatus) {
791     return false;
792   }
793   // Disable this on all Intel devices because of crashes.
794   // See bug 1292923.
795   return mDeviceStatus->adapter().VendorId != 0x8086;
796 }
797 
798 bool
CheckRemotePresentSupport()799 DeviceManagerDx::CheckRemotePresentSupport()
800 {
801   MOZ_ASSERT(XRE_IsParentProcess());
802 
803   RefPtr<IDXGIAdapter1> adapter = GetDXGIAdapter();
804   if (!adapter) {
805     return false;
806   }
807   if (!D3D11Checks::DoesRemotePresentWork(adapter)) {
808     return false;
809   }
810   return true;
811 }
812 
813 bool
IsWARP()814 DeviceManagerDx::IsWARP()
815 {
816   MutexAutoLock lock(mDeviceLock);
817   if (!mDeviceStatus) {
818     return false;
819   }
820   return mDeviceStatus->isWARP();
821 }
822 
823 void
InitializeDirectDraw()824 DeviceManagerDx::InitializeDirectDraw()
825 {
826   MOZ_ASSERT(layers::CompositorThreadHolder::IsInCompositorThread());
827 
828   if (mDirectDraw) {
829     // Already initialized.
830     return;
831   }
832 
833   FeatureState& ddraw = gfxConfig::GetFeature(Feature::DIRECT_DRAW);
834   if (!ddraw.IsEnabled()) {
835     return;
836   }
837 
838   // Check if DirectDraw is available on this system.
839   mDirectDrawDLL.own(LoadLibrarySystem32(L"ddraw.dll"));
840   if (!mDirectDrawDLL) {
841     ddraw.SetFailed(FeatureStatus::Unavailable, "DirectDraw not available on this computer",
842                     NS_LITERAL_CSTRING("FEATURE_FAILURE_DDRAW_LIB"));
843     return;
844   }
845 
846   sDirectDrawCreateExFn =
847     (decltype(DirectDrawCreateEx)*)GetProcAddress(mDirectDrawDLL, "DirectDrawCreateEx");
848   if (!sDirectDrawCreateExFn) {
849     ddraw.SetFailed(FeatureStatus::Unavailable, "DirectDraw not available on this computer",
850                     NS_LITERAL_CSTRING("FEATURE_FAILURE_DDRAW_LIB"));
851     return;
852   }
853 
854   HRESULT hr;
855   MOZ_SEH_TRY {
856     hr = sDirectDrawCreateExFn(nullptr, getter_AddRefs(mDirectDraw), IID_IDirectDraw7, nullptr);
857   } MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
858     ddraw.SetFailed(FeatureStatus::Failed, "Failed to create DirectDraw",
859                     NS_LITERAL_CSTRING("FEATURE_FAILURE_DDRAW_LIB"));
860     gfxCriticalNote << "DoesCreatingDirectDrawFailed";
861     return;
862   }
863   if (FAILED(hr)) {
864     ddraw.SetFailed(FeatureStatus::Failed, "Failed to create DirectDraw",
865                     NS_LITERAL_CSTRING("FEATURE_FAILURE_DDRAW_LIB"));
866     gfxCriticalNote << "DoesCreatingDirectDrawFailed " << hexa(hr);
867     return;
868   }
869 }
870 
871 IDirectDraw7*
GetDirectDraw()872 DeviceManagerDx::GetDirectDraw()
873 {
874   return mDirectDraw;
875 }
876 
877 } // namespace gfx
878 } // namespace mozilla
879