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/GPUParent.h"
16 #include "mozilla/gfx/GraphicsMessages.h"
17 #include "mozilla/gfx/Logging.h"
18 #include "mozilla/gfx/gfxVars.h"
19 #include "mozilla/layers/CompositorBridgeChild.h"
20 #include "mozilla/layers/CompositorThread.h"
21 #include "mozilla/layers/DeviceAttachmentsD3D11.h"
22 #include "mozilla/layers/MLGDeviceD3D11.h"
23 #include "mozilla/layers/PaintThread.h"
24 #include "nsExceptionHandler.h"
25 #include "nsIGfxInfo.h"
26 #include "nsPrintfCString.h"
27 #include "nsString.h"
28 #include <d3d11.h>
29 #include <ddraw.h>
30 
31 namespace mozilla {
32 namespace gfx {
33 
34 using namespace mozilla::widget;
35 using namespace mozilla::layers;
36 
37 StaticAutoPtr<DeviceManagerDx> DeviceManagerDx::sInstance;
38 
39 // We don't have access to the D3D11CreateDevice type in gfxWindowsPlatform.h,
40 // since it doesn't include d3d11.h, so we use a static here. It should only
41 // be used within InitializeD3D11.
42 decltype(D3D11CreateDevice)* sD3D11CreateDeviceFn = nullptr;
43 
44 // We don't have access to the DirectDrawCreateEx type in gfxWindowsPlatform.h,
45 // since it doesn't include ddraw.h, so we use a static here. It should only
46 // be used within InitializeDirectDrawConfig.
47 decltype(DirectDrawCreateEx)* sDirectDrawCreateExFn = nullptr;
48 
Init()49 /* static */ void DeviceManagerDx::Init() { sInstance = new DeviceManagerDx(); }
50 
Shutdown()51 /* static */ void DeviceManagerDx::Shutdown() { sInstance = nullptr; }
52 
DeviceManagerDx()53 DeviceManagerDx::DeviceManagerDx()
54     : mDeviceLock("gfxWindowsPlatform.mDeviceLock"),
55       mCompositorDeviceSupportsVideo(false) {
56   // Set up the D3D11 feature levels we can ask for.
57   if (IsWin8OrLater()) {
58     mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_11_1);
59   }
60   mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_11_0);
61   mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_10_1);
62   mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_10_0);
63 }
64 
LoadD3D11()65 bool DeviceManagerDx::LoadD3D11() {
66   FeatureState& d3d11 = gfxConfig::GetFeature(Feature::D3D11_COMPOSITING);
67   MOZ_ASSERT(d3d11.IsEnabled());
68 
69   if (sD3D11CreateDeviceFn) {
70     return true;
71   }
72 
73   nsModuleHandle module(LoadLibrarySystem32(L"d3d11.dll"));
74   if (!module) {
75     d3d11.SetFailed(FeatureStatus::Unavailable,
76                     "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,
86                     "Direct3D11 not available on this computer",
87                     NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_FUNCPTR"));
88     return false;
89   }
90 
91   mD3D11Module.steal(module);
92   return true;
93 }
94 
ReleaseD3D11()95 void DeviceManagerDx::ReleaseD3D11() {
96   MOZ_ASSERT(!mCompositorDevice);
97   MOZ_ASSERT(!mContentDevice);
98   MOZ_ASSERT(!mVRDevice);
99   MOZ_ASSERT(!mDecoderDevice);
100 
101   mD3D11Module.reset();
102   sD3D11CreateDeviceFn = nullptr;
103 }
104 
ProcessOwnsCompositor()105 static inline bool ProcessOwnsCompositor() {
106   return XRE_GetProcessType() == GeckoProcessType_GPU ||
107          (XRE_IsParentProcess() && !gfxConfig::IsEnabled(Feature::GPU_PROCESS));
108 }
109 
CreateCompositorDevices()110 bool DeviceManagerDx::CreateCompositorDevices() {
111   MOZ_ASSERT(ProcessOwnsCompositor());
112 
113   FeatureState& d3d11 = gfxConfig::GetFeature(Feature::D3D11_COMPOSITING);
114   MOZ_ASSERT(d3d11.IsEnabled());
115 
116   if (int32_t sleepSec = gfxPrefs::Direct3D11SleepOnCreateDevice()) {
117     printf_stderr("Attach to PID: %d\n", GetCurrentProcessId());
118     Sleep(sleepSec * 1000);
119   }
120 
121   if (!LoadD3D11()) {
122     return false;
123   }
124 
125   CreateCompositorDevice(d3d11);
126 
127   if (!d3d11.IsEnabled()) {
128     MOZ_ASSERT(!mCompositorDevice);
129     ReleaseD3D11();
130 
131     // Sync Advanced-Layers with D3D11.
132     if (gfxConfig::IsEnabled(Feature::ADVANCED_LAYERS)) {
133       gfxConfig::SetFailed(Feature::ADVANCED_LAYERS, FeatureStatus::Unavailable,
134                            "Requires D3D11",
135                            NS_LITERAL_CSTRING("FEATURE_FAILURE_NO_D3D11"));
136     }
137     return false;
138   }
139 
140   // We leak these everywhere and we need them our entire runtime anyway, let's
141   // leak it here as well. We keep the pointer to sD3D11CreateDeviceFn around
142   // as well for D2D1 and device resets.
143   mD3D11Module.disown();
144 
145   MOZ_ASSERT(mCompositorDevice);
146   if (!d3d11.IsEnabled()) {
147     return false;
148   }
149 
150   PreloadAttachmentsOnCompositorThread();
151   return true;
152 }
153 
CreateVRDevice()154 bool DeviceManagerDx::CreateVRDevice() {
155   MOZ_ASSERT(ProcessOwnsCompositor());
156 
157   if (mVRDevice) {
158     return true;
159   }
160 
161   if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
162     NS_WARNING("Direct3D11 Compositing required for VR");
163     return false;
164   }
165 
166   if (!LoadD3D11()) {
167     return false;
168   }
169 
170   RefPtr<IDXGIAdapter1> adapter = GetDXGIAdapter();
171   if (!adapter) {
172     NS_WARNING("Failed to acquire a DXGI adapter for VR");
173     return false;
174   }
175 
176   UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
177 
178   HRESULT hr;
179   if (!CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, flags, hr, mVRDevice)) {
180     gfxCriticalError() << "Crash during D3D11 device creation for VR";
181     return false;
182   }
183 
184   if (FAILED(hr) || !mVRDevice) {
185     NS_WARNING("Failed to acquire a D3D11 device for VR");
186     return false;
187   }
188 
189   return true;
190 }
191 
ImportDeviceInfo(const D3D11DeviceStatus & aDeviceStatus)192 void DeviceManagerDx::ImportDeviceInfo(const D3D11DeviceStatus& aDeviceStatus) {
193   MOZ_ASSERT(!ProcessOwnsCompositor());
194 
195   mDeviceStatus = Some(aDeviceStatus);
196 }
197 
ExportDeviceInfo(D3D11DeviceStatus * aOut)198 void DeviceManagerDx::ExportDeviceInfo(D3D11DeviceStatus* aOut) {
199   if (mDeviceStatus) {
200     *aOut = mDeviceStatus.value();
201   }
202 }
203 
CreateContentDevices()204 void DeviceManagerDx::CreateContentDevices() {
205   MOZ_ASSERT(gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING));
206 
207   if (!LoadD3D11()) {
208     return;
209   }
210 
211   // We should have been assigned a DeviceStatus from the parent process,
212   // GPU process, or the same process if using in-process compositing.
213   MOZ_ASSERT(mDeviceStatus);
214 
215   if (CreateContentDevice() == FeatureStatus::CrashedInHandler) {
216     DisableD3D11AfterCrash();
217   }
218 }
219 
GetDXGIAdapter()220 IDXGIAdapter1* DeviceManagerDx::GetDXGIAdapter() {
221   if (mAdapter) {
222     return mAdapter;
223   }
224 
225   nsModuleHandle dxgiModule(LoadLibrarySystem32(L"dxgi.dll"));
226   decltype(CreateDXGIFactory1)* createDXGIFactory1 =
227       (decltype(CreateDXGIFactory1)*)GetProcAddress(dxgiModule,
228                                                     "CreateDXGIFactory1");
229 
230   if (!createDXGIFactory1) {
231     return nullptr;
232   }
233 
234   // Try to use a DXGI 1.1 adapter in order to share resources
235   // across processes.
236   RefPtr<IDXGIFactory1> factory1;
237   HRESULT hr =
238       createDXGIFactory1(__uuidof(IDXGIFactory1), getter_AddRefs(factory1));
239   if (FAILED(hr) || !factory1) {
240     // This seems to happen with some people running the iZ3D driver.
241     // They won't get acceleration.
242     return nullptr;
243   }
244 
245   if (!mDeviceStatus) {
246     // If we haven't created a device yet, and have no existing device status,
247     // then this must be the compositor device. Pick the first adapter we can.
248     if (FAILED(factory1->EnumAdapters1(0, getter_AddRefs(mAdapter)))) {
249       return nullptr;
250     }
251   } else {
252     // In the UI and GPU process, we clear mDeviceStatus on device reset, so we
253     // should never reach here. Furthermore, the UI process does not create
254     // devices when using a GPU process.
255     //
256     // So, this should only ever get called on the content process.
257     MOZ_ASSERT(XRE_IsContentProcess());
258 
259     // In the child process, we search for the adapter that matches the parent
260     // process. The first adapter can be mismatched on dual-GPU systems.
261     for (UINT index = 0;; index++) {
262       RefPtr<IDXGIAdapter1> adapter;
263       if (FAILED(factory1->EnumAdapters1(index, getter_AddRefs(adapter)))) {
264         break;
265       }
266 
267       const DxgiAdapterDesc& preferred = mDeviceStatus->adapter();
268 
269       DXGI_ADAPTER_DESC desc;
270       if (SUCCEEDED(adapter->GetDesc(&desc)) &&
271           desc.AdapterLuid.HighPart == preferred.AdapterLuid.HighPart &&
272           desc.AdapterLuid.LowPart == preferred.AdapterLuid.LowPart &&
273           desc.VendorId == preferred.VendorId &&
274           desc.DeviceId == preferred.DeviceId) {
275         mAdapter = adapter.forget();
276         break;
277       }
278     }
279   }
280 
281   if (!mAdapter) {
282     return nullptr;
283   }
284 
285   // We leak this module everywhere, we might as well do so here as well.
286   dxgiModule.disown();
287   return mAdapter;
288 }
289 
CreateCompositorDeviceHelper(FeatureState & aD3d11,IDXGIAdapter1 * aAdapter,bool aAttemptVideoSupport,RefPtr<ID3D11Device> & aOutDevice)290 bool DeviceManagerDx::CreateCompositorDeviceHelper(
291     FeatureState& aD3d11, IDXGIAdapter1* aAdapter, bool aAttemptVideoSupport,
292     RefPtr<ID3D11Device>& aOutDevice) {
293   // Check if a failure was injected for testing.
294   if (gfxPrefs::DeviceFailForTesting()) {
295     aD3d11.SetFailed(FeatureStatus::Failed,
296                      "Direct3D11 device failure simulated by preference",
297                      NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_SIM"));
298     return false;
299   }
300 
301   // Use D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS
302   // to prevent bug 1092260. IE 11 also uses this flag.
303   UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT |
304                D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS;
305   if (aAttemptVideoSupport) {
306     flags |= D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
307   }
308 
309   HRESULT hr;
310   RefPtr<ID3D11Device> device;
311   if (!CreateDevice(aAdapter, D3D_DRIVER_TYPE_UNKNOWN, flags, hr, device)) {
312     if (!aAttemptVideoSupport) {
313       gfxCriticalError() << "Crash during D3D11 device creation";
314       aD3d11.SetFailed(FeatureStatus::CrashedInHandler,
315                        "Crashed trying to acquire a D3D11 device",
316                        NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_DEVICE1"));
317     }
318     return false;
319   }
320 
321   if (FAILED(hr) || !device) {
322     if (!aAttemptVideoSupport) {
323       aD3d11.SetFailed(FeatureStatus::Failed,
324                        "Failed to acquire a D3D11 device",
325                        NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_DEVICE2"));
326     }
327     return false;
328   }
329   if (!D3D11Checks::DoesDeviceWork()) {
330     if (!aAttemptVideoSupport) {
331       aD3d11.SetFailed(FeatureStatus::Broken,
332                        "Direct3D11 device was determined to be broken",
333                        NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_BROKEN"));
334     }
335     return false;
336   }
337 
338   aOutDevice = device;
339   return true;
340 }
341 
342 // Note that it's enough for us to just use a counter for a unique ID,
343 // even though the counter isn't synchronized between processes. If we
344 // start in the GPU process and wind up in the parent process, the
345 // whole graphics stack is blown away anyway. But just in case, we
346 // make gpu process IDs negative and parent process IDs positive.
GetNextDeviceCounter()347 static inline int32_t GetNextDeviceCounter() {
348   static int32_t sDeviceCounter = 0;
349   return XRE_IsGPUProcess() ? --sDeviceCounter : ++sDeviceCounter;
350 }
351 
CreateCompositorDevice(FeatureState & d3d11)352 void DeviceManagerDx::CreateCompositorDevice(FeatureState& d3d11) {
353   if (gfxPrefs::LayersD3D11ForceWARP()) {
354     CreateWARPCompositorDevice();
355     return;
356   }
357 
358   RefPtr<IDXGIAdapter1> adapter = GetDXGIAdapter();
359   if (!adapter) {
360     d3d11.SetFailed(FeatureStatus::Unavailable,
361                     "Failed to acquire a DXGI adapter",
362                     NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_DXGI"));
363     return;
364   }
365 
366   if (XRE_IsGPUProcess() && !D3D11Checks::DoesRemotePresentWork(adapter)) {
367     d3d11.SetFailed(FeatureStatus::Unavailable,
368                     "DXGI does not support out-of-process presentation",
369                     NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_REMOTE_PRESENT"));
370     return;
371   }
372 
373   RefPtr<ID3D11Device> device;
374   if (!CreateCompositorDeviceHelper(d3d11, adapter, true, device)) {
375     // Try again without video support and record that it failed.
376     mCompositorDeviceSupportsVideo = false;
377     if (!CreateCompositorDeviceHelper(d3d11, adapter, false, device)) {
378       return;
379     }
380   } else {
381     mCompositorDeviceSupportsVideo = true;
382   }
383 
384   // Only test this when not using WARP since it can fail and cause
385   // GetDeviceRemovedReason to return weird values.
386   bool textureSharingWorks = D3D11Checks::DoesTextureSharingWork(device);
387 
388   DXGI_ADAPTER_DESC desc;
389   PodZero(&desc);
390   adapter->GetDesc(&desc);
391 
392   if (!textureSharingWorks) {
393     gfxConfig::SetFailed(Feature::D3D11_HW_ANGLE, FeatureStatus::Broken,
394                          "Texture sharing doesn't work");
395   }
396   if (D3D11Checks::DoesRenderTargetViewNeedRecreating(device)) {
397     gfxConfig::SetFailed(Feature::D3D11_HW_ANGLE, FeatureStatus::Broken,
398                          "RenderTargetViews need recreating");
399   }
400   if (XRE_IsParentProcess()) {
401     // It seems like this may only happen when we're using the NVIDIA gpu
402     D3D11Checks::WarnOnAdapterMismatch(device);
403   }
404 
405   uint32_t featureLevel = device->GetFeatureLevel();
406   bool useNV12 = D3D11Checks::DoesNV12Work(device);
407   {
408     MutexAutoLock lock(mDeviceLock);
409     mCompositorDevice = device;
410 
411     int32_t sequenceNumber = GetNextDeviceCounter();
412     mDeviceStatus = Some(D3D11DeviceStatus(
413         false, textureSharingWorks, featureLevel, DxgiAdapterDesc::From(desc),
414         sequenceNumber, useNV12));
415   }
416   mCompositorDevice->SetExceptionMode(0);
417 }
418 
CreateDevice(IDXGIAdapter * aAdapter,D3D_DRIVER_TYPE aDriverType,UINT aFlags,HRESULT & aResOut,RefPtr<ID3D11Device> & aOutDevice)419 bool DeviceManagerDx::CreateDevice(IDXGIAdapter* aAdapter,
420                                    D3D_DRIVER_TYPE aDriverType, UINT aFlags,
421                                    HRESULT& aResOut,
422                                    RefPtr<ID3D11Device>& aOutDevice) {
423   if (gfxPrefs::Direct3D11EnableDebugLayer() ||
424       gfxPrefs::Direct3D11BreakOnError()) {
425     aFlags |= D3D11_CREATE_DEVICE_DEBUG;
426   }
427 
428   MOZ_SEH_TRY {
429     aResOut = sD3D11CreateDeviceFn(
430         aAdapter, aDriverType, nullptr, aFlags, mFeatureLevels.Elements(),
431         mFeatureLevels.Length(), D3D11_SDK_VERSION, getter_AddRefs(aOutDevice),
432         nullptr, nullptr);
433   }
434   MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { return false; }
435 
436   if (gfxPrefs::Direct3D11BreakOnError()) {
437     do {
438       if (!aOutDevice) break;
439 
440       RefPtr<ID3D11Debug> debug;
441       if (!SUCCEEDED(aOutDevice->QueryInterface(__uuidof(ID3D11Debug),
442                                                 getter_AddRefs(debug))))
443         break;
444 
445       RefPtr<ID3D11InfoQueue> infoQueue;
446       if (!SUCCEEDED(debug->QueryInterface(__uuidof(ID3D11InfoQueue),
447                                            getter_AddRefs(infoQueue))))
448         break;
449 
450       D3D11_INFO_QUEUE_FILTER filter;
451       PodZero(&filter);
452 
453       // Disable warnings caused by Advanced Layers that are known and not
454       // problematic.
455       D3D11_MESSAGE_ID blockIDs[] = {
456           D3D11_MESSAGE_ID_DEVICE_DRAW_CONSTANT_BUFFER_TOO_SMALL};
457       filter.DenyList.NumIDs = MOZ_ARRAY_LENGTH(blockIDs);
458       filter.DenyList.pIDList = blockIDs;
459       infoQueue->PushStorageFilter(&filter);
460 
461       infoQueue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_CORRUPTION, true);
462       infoQueue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, true);
463       infoQueue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_WARNING, true);
464     } while (false);
465   }
466 
467   return true;
468 }
469 
CreateWARPCompositorDevice()470 void DeviceManagerDx::CreateWARPCompositorDevice() {
471   ScopedGfxFeatureReporter reporterWARP("D3D11-WARP",
472                                         gfxPrefs::LayersD3D11ForceWARP());
473   FeatureState& d3d11 = gfxConfig::GetFeature(Feature::D3D11_COMPOSITING);
474 
475   HRESULT hr;
476   RefPtr<ID3D11Device> device;
477 
478   // Use D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS
479   // to prevent bug 1092260. IE 11 also uses this flag.
480   UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
481   if (!CreateDevice(nullptr, D3D_DRIVER_TYPE_WARP, flags, hr, device)) {
482     gfxCriticalError() << "Exception occurred initializing WARP D3D11 device!";
483     d3d11.SetFailed(FeatureStatus::CrashedInHandler,
484                     "Crashed creating a D3D11 WARP device",
485                     NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_WARP_DEVICE"));
486   }
487 
488   if (FAILED(hr) || !device) {
489     // This should always succeed... in theory.
490     gfxCriticalError() << "Failed to initialize WARP D3D11 device! "
491                        << hexa(hr);
492     d3d11.SetFailed(FeatureStatus::Failed,
493                     "Failed to create a D3D11 WARP device",
494                     NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_WARP_DEVICE2"));
495     return;
496   }
497 
498   // Only test for texture sharing on Windows 8 since it puts the device into
499   // an unusable state if used on Windows 7
500   bool textureSharingWorks = false;
501   if (IsWin8OrLater()) {
502     textureSharingWorks = D3D11Checks::DoesTextureSharingWork(device);
503   }
504 
505   DxgiAdapterDesc nullAdapter;
506   PodZero(&nullAdapter);
507 
508   int featureLevel = device->GetFeatureLevel();
509 
510   bool useNV12 = D3D11Checks::DoesNV12Work(device);
511   {
512     MutexAutoLock lock(mDeviceLock);
513     mCompositorDevice = device;
514 
515     int32_t sequenceNumber = GetNextDeviceCounter();
516     mDeviceStatus =
517         Some(D3D11DeviceStatus(true, textureSharingWorks, featureLevel,
518                                nullAdapter, sequenceNumber, useNV12));
519   }
520   mCompositorDevice->SetExceptionMode(0);
521 
522   reporterWARP.SetSuccessful();
523 }
524 
CreateContentDevice()525 FeatureStatus DeviceManagerDx::CreateContentDevice() {
526   RefPtr<IDXGIAdapter1> adapter;
527   if (!mDeviceStatus->isWARP()) {
528     adapter = GetDXGIAdapter();
529     if (!adapter) {
530       gfxCriticalNote << "Could not get a DXGI adapter";
531       return FeatureStatus::Unavailable;
532     }
533   }
534 
535   HRESULT hr;
536   RefPtr<ID3D11Device> device;
537 
538   UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
539   D3D_DRIVER_TYPE type =
540       mDeviceStatus->isWARP() ? D3D_DRIVER_TYPE_WARP : D3D_DRIVER_TYPE_UNKNOWN;
541   if (!CreateDevice(adapter, type, flags, hr, device)) {
542     gfxCriticalNote
543         << "Recovered from crash while creating a D3D11 content device";
544     gfxWindowsPlatform::RecordContentDeviceFailure(
545         TelemetryDeviceCode::Content);
546     return FeatureStatus::CrashedInHandler;
547   }
548 
549   if (FAILED(hr) || !device) {
550     gfxCriticalNote << "Failed to create a D3D11 content device: " << hexa(hr);
551     gfxWindowsPlatform::RecordContentDeviceFailure(
552         TelemetryDeviceCode::Content);
553     return FeatureStatus::Failed;
554   }
555 
556   // InitializeD2D() will abort early if the compositor device did not support
557   // texture sharing. If we're in the content process, we can't rely on the
558   // parent device alone: some systems have dual GPUs that are capable of
559   // binding the parent and child processes to different GPUs. As a safety net,
560   // we re-check texture sharing against the newly created D3D11 content device.
561   // If it fails, we won't use Direct2D.
562   if (XRE_IsContentProcess()) {
563     if (!D3D11Checks::DoesTextureSharingWork(device)) {
564       return FeatureStatus::Failed;
565     }
566 
567     DebugOnly<bool> ok = ContentAdapterIsParentAdapter(device);
568     MOZ_ASSERT(ok);
569   }
570 
571   {
572     MutexAutoLock lock(mDeviceLock);
573     mContentDevice = device;
574   }
575   mContentDevice->SetExceptionMode(0);
576 
577   RefPtr<ID3D10Multithread> multi;
578   hr = mContentDevice->QueryInterface(__uuidof(ID3D10Multithread),
579                                       getter_AddRefs(multi));
580   if (SUCCEEDED(hr) && multi) {
581     multi->SetMultithreadProtected(TRUE);
582   }
583   return FeatureStatus::Available;
584 }
585 
CreateDecoderDevice()586 RefPtr<ID3D11Device> DeviceManagerDx::CreateDecoderDevice() {
587   bool isAMD = false;
588   {
589     MutexAutoLock lock(mDeviceLock);
590     if (!mDeviceStatus) {
591       return nullptr;
592     }
593     isAMD = mDeviceStatus->adapter().VendorId == 0x1002;
594   }
595 
596   bool reuseDevice = false;
597   if (gfxPrefs::Direct3D11ReuseDecoderDevice() < 0) {
598     // Use the default logic, which is to allow reuse of devices on AMD, but
599     // create separate devices everywhere else.
600     if (isAMD) {
601       reuseDevice = true;
602     }
603   } else if (gfxPrefs::Direct3D11ReuseDecoderDevice() > 0) {
604     reuseDevice = true;
605   }
606 
607   if (reuseDevice) {
608     if (mCompositorDevice && mCompositorDeviceSupportsVideo &&
609         !mDecoderDevice) {
610       mDecoderDevice = mCompositorDevice;
611 
612       RefPtr<ID3D10Multithread> multi;
613       mDecoderDevice->QueryInterface(__uuidof(ID3D10Multithread),
614                                      getter_AddRefs(multi));
615       if (multi) {
616         multi->SetMultithreadProtected(TRUE);
617       }
618     }
619 
620     if (mDecoderDevice) {
621       RefPtr<ID3D11Device> dev = mDecoderDevice;
622       return dev.forget();
623     }
624   }
625 
626   if (!sD3D11CreateDeviceFn) {
627     // We should just be on Windows Vista or XP in this case.
628     return nullptr;
629   }
630 
631   RefPtr<IDXGIAdapter1> adapter = GetDXGIAdapter();
632   if (!adapter) {
633     return nullptr;
634   }
635 
636   HRESULT hr;
637   RefPtr<ID3D11Device> device;
638 
639   UINT flags = D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS |
640                D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
641   if (!CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, flags, hr, device)) {
642     return nullptr;
643   }
644   if (FAILED(hr) || !device || !D3D11Checks::DoesDeviceWork()) {
645     return nullptr;
646   }
647 
648   RefPtr<ID3D10Multithread> multi;
649   device->QueryInterface(__uuidof(ID3D10Multithread), getter_AddRefs(multi));
650   if (multi) {
651     multi->SetMultithreadProtected(TRUE);
652   }
653   if (reuseDevice) {
654     mDecoderDevice = device;
655   }
656   return device;
657 }
658 
GetMLGDevice()659 RefPtr<MLGDevice> DeviceManagerDx::GetMLGDevice() {
660   MutexAutoLock lock(mDeviceLock);
661   if (!mMLGDevice) {
662     MutexAutoUnlock unlock(mDeviceLock);
663     CreateMLGDevice();
664   }
665   return mMLGDevice;
666 }
667 
DisableAdvancedLayers(FeatureStatus aStatus,const nsCString aMessage,const nsCString & aFailureId)668 static void DisableAdvancedLayers(FeatureStatus aStatus,
669                                   const nsCString aMessage,
670                                   const nsCString& aFailureId) {
671   if (!NS_IsMainThread()) {
672     NS_DispatchToMainThread(NS_NewRunnableFunction(
673         "DisableAdvancedLayers", [aStatus, aMessage, aFailureId]() -> void {
674           DisableAdvancedLayers(aStatus, aMessage, aFailureId);
675         }));
676     return;
677   }
678 
679   MOZ_ASSERT(NS_IsMainThread());
680 
681   FeatureState& al = gfxConfig::GetFeature(Feature::ADVANCED_LAYERS);
682   if (!al.IsEnabled()) {
683     return;
684   }
685 
686   al.SetFailed(aStatus, aMessage.get(), aFailureId);
687 
688   FeatureFailure info(aStatus, aMessage, aFailureId);
689   if (GPUParent* gpu = GPUParent::GetSingleton()) {
690     Unused << gpu->SendUpdateFeature(Feature::ADVANCED_LAYERS, info);
691   }
692 
693   if (aFailureId.Length()) {
694     nsString failureId = NS_ConvertUTF8toUTF16(aFailureId.get());
695     Telemetry::ScalarAdd(Telemetry::ScalarID::GFX_ADVANCED_LAYERS_FAILURE_ID,
696                          failureId, 1);
697   }
698 
699   // Notify TelemetryEnvironment.jsm.
700   if (RefPtr<nsIObserverService> obs =
701           mozilla::services::GetObserverService()) {
702     obs->NotifyObservers(nullptr, "gfx-features-ready", nullptr);
703   }
704 }
705 
CreateMLGDevice()706 void DeviceManagerDx::CreateMLGDevice() {
707   MOZ_ASSERT(layers::CompositorThreadHolder::IsInCompositorThread());
708 
709   RefPtr<ID3D11Device> d3d11Device = GetCompositorDevice();
710   if (!d3d11Device) {
711     DisableAdvancedLayers(
712         FeatureStatus::Unavailable,
713         NS_LITERAL_CSTRING("Advanced-layers requires a D3D11 device"),
714         NS_LITERAL_CSTRING("FEATURE_FAILURE_NEED_D3D11_DEVICE"));
715     return;
716   }
717 
718   RefPtr<MLGDeviceD3D11> device = new MLGDeviceD3D11(d3d11Device);
719   if (!device->Initialize()) {
720     DisableAdvancedLayers(FeatureStatus::Failed, device->GetFailureMessage(),
721                           device->GetFailureId());
722     return;
723   }
724 
725   // While the lock was unheld, we should not have created an MLGDevice, since
726   // this should only be called on the compositor thread.
727   MutexAutoLock lock(mDeviceLock);
728   MOZ_ASSERT(!mMLGDevice);
729 
730   // Only set the MLGDevice if the compositor device is still the same.
731   // Otherwise we could possibly have a bad MLGDevice if a device reset
732   // just occurred.
733   if (mCompositorDevice == d3d11Device) {
734     mMLGDevice = device;
735   }
736 }
737 
ResetDevices()738 void DeviceManagerDx::ResetDevices() {
739   // Flush the paint thread before revoking all these singletons. This
740   // should ensure that the paint thread doesn't start mixing and matching
741   // old and new objects together.
742   if (PaintThread::Get()) {
743     CompositorBridgeChild* cbc = CompositorBridgeChild::Get();
744     if (cbc) {
745       cbc->FlushAsyncPaints();
746     }
747   }
748 
749   MutexAutoLock lock(mDeviceLock);
750 
751   mAdapter = nullptr;
752   mCompositorAttachments = nullptr;
753   mMLGDevice = nullptr;
754   mCompositorDevice = nullptr;
755   mContentDevice = nullptr;
756   mDeviceStatus = Nothing();
757   mDeviceResetReason = Nothing();
758   Factory::SetDirect3D11Device(nullptr);
759 }
760 
MaybeResetAndReacquireDevices()761 bool DeviceManagerDx::MaybeResetAndReacquireDevices() {
762   DeviceResetReason resetReason;
763   if (!HasDeviceReset(&resetReason)) {
764     return false;
765   }
766 
767   if (resetReason != DeviceResetReason::FORCED_RESET) {
768     Telemetry::Accumulate(Telemetry::DEVICE_RESET_REASON,
769                           uint32_t(resetReason));
770   }
771 
772   nsPrintfCString reasonString("%d", int(resetReason));
773   CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("DeviceResetReason"),
774                                      reasonString);
775 
776   bool createCompositorDevice = !!mCompositorDevice;
777   bool createContentDevice = !!mContentDevice;
778 
779   ResetDevices();
780 
781   if (createCompositorDevice && !CreateCompositorDevices()) {
782     // Just stop, don't try anything more
783     return true;
784   }
785   if (createContentDevice) {
786     CreateContentDevices();
787   }
788 
789   return true;
790 }
791 
ContentAdapterIsParentAdapter(ID3D11Device * device)792 bool DeviceManagerDx::ContentAdapterIsParentAdapter(ID3D11Device* device) {
793   DXGI_ADAPTER_DESC desc;
794   if (!D3D11Checks::GetDxgiDesc(device, &desc)) {
795     gfxCriticalNote << "Could not query device DXGI adapter info";
796     return false;
797   }
798 
799   const DxgiAdapterDesc& preferred = mDeviceStatus->adapter();
800 
801   if (desc.VendorId != preferred.VendorId ||
802       desc.DeviceId != preferred.DeviceId ||
803       desc.SubSysId != preferred.SubSysId ||
804       desc.AdapterLuid.HighPart != preferred.AdapterLuid.HighPart ||
805       desc.AdapterLuid.LowPart != preferred.AdapterLuid.LowPart) {
806     gfxCriticalNote << "VendorIDMismatch P " << hexa(preferred.VendorId) << " "
807                     << hexa(desc.VendorId);
808     return false;
809   }
810 
811   return true;
812 }
813 
HResultToResetReason(HRESULT hr)814 static DeviceResetReason HResultToResetReason(HRESULT hr) {
815   switch (hr) {
816     case DXGI_ERROR_DEVICE_HUNG:
817       return DeviceResetReason::HUNG;
818     case DXGI_ERROR_DEVICE_REMOVED:
819       return DeviceResetReason::REMOVED;
820     case DXGI_ERROR_DEVICE_RESET:
821       return DeviceResetReason::RESET;
822     case DXGI_ERROR_DRIVER_INTERNAL_ERROR:
823       return DeviceResetReason::DRIVER_ERROR;
824     case DXGI_ERROR_INVALID_CALL:
825       return DeviceResetReason::INVALID_CALL;
826     case E_OUTOFMEMORY:
827       return DeviceResetReason::OUT_OF_MEMORY;
828     default:
829       MOZ_ASSERT(false);
830   }
831   return DeviceResetReason::UNKNOWN;
832 }
833 
HasDeviceReset(DeviceResetReason * aOutReason)834 bool DeviceManagerDx::HasDeviceReset(DeviceResetReason* aOutReason) {
835   MutexAutoLock lock(mDeviceLock);
836 
837   if (mDeviceResetReason) {
838     if (aOutReason) {
839       *aOutReason = mDeviceResetReason.value();
840     }
841     return true;
842   }
843 
844   DeviceResetReason reason;
845   if (GetAnyDeviceRemovedReason(&reason)) {
846     mDeviceResetReason = Some(reason);
847     if (aOutReason) {
848       *aOutReason = reason;
849     }
850     return true;
851   }
852 
853   return false;
854 }
855 
DidDeviceReset(const RefPtr<ID3D11Device> & aDevice,DeviceResetReason * aOutReason)856 static inline bool DidDeviceReset(const RefPtr<ID3D11Device>& aDevice,
857                                   DeviceResetReason* aOutReason) {
858   if (!aDevice) {
859     return false;
860   }
861   HRESULT hr = aDevice->GetDeviceRemovedReason();
862   if (hr == S_OK) {
863     return false;
864   }
865 
866   *aOutReason = HResultToResetReason(hr);
867   return true;
868 }
869 
GetAnyDeviceRemovedReason(DeviceResetReason * aOutReason)870 bool DeviceManagerDx::GetAnyDeviceRemovedReason(DeviceResetReason* aOutReason) {
871   // Caller must own the lock, since we access devices directly, and can be
872   // called from any thread.
873   mDeviceLock.AssertCurrentThreadOwns();
874 
875   if (DidDeviceReset(mCompositorDevice, aOutReason) ||
876       DidDeviceReset(mContentDevice, aOutReason)) {
877     return true;
878   }
879 
880   if (XRE_IsParentProcess() && NS_IsMainThread() &&
881       gfxPrefs::DeviceResetForTesting()) {
882     gfxPrefs::SetDeviceResetForTesting(0);
883     *aOutReason = DeviceResetReason::FORCED_RESET;
884     return true;
885   }
886 
887   return false;
888 }
889 
ForceDeviceReset(ForcedDeviceResetReason aReason)890 void DeviceManagerDx::ForceDeviceReset(ForcedDeviceResetReason aReason) {
891   Telemetry::Accumulate(Telemetry::FORCED_DEVICE_RESET_REASON,
892                         uint32_t(aReason));
893   {
894     MutexAutoLock lock(mDeviceLock);
895     if (!mDeviceResetReason) {
896       mDeviceResetReason = Some(DeviceResetReason::FORCED_RESET);
897     }
898   }
899 }
900 
DisableD3D11AfterCrash()901 void DeviceManagerDx::DisableD3D11AfterCrash() {
902   gfxConfig::Disable(Feature::D3D11_COMPOSITING,
903                      FeatureStatus::CrashedInHandler,
904                      "Crashed while acquiring a Direct3D11 device",
905                      NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_CRASH"));
906   ResetDevices();
907 }
908 
GetCompositorDevice()909 RefPtr<ID3D11Device> DeviceManagerDx::GetCompositorDevice() {
910   MutexAutoLock lock(mDeviceLock);
911   return mCompositorDevice;
912 }
913 
GetContentDevice()914 RefPtr<ID3D11Device> DeviceManagerDx::GetContentDevice() {
915   MutexAutoLock lock(mDeviceLock);
916   return mContentDevice;
917 }
918 
GetVRDevice()919 RefPtr<ID3D11Device> DeviceManagerDx::GetVRDevice() {
920   MutexAutoLock lock(mDeviceLock);
921   if (!mVRDevice) {
922     CreateVRDevice();
923   }
924   return mVRDevice;
925 }
926 
GetCompositorFeatureLevel() const927 unsigned DeviceManagerDx::GetCompositorFeatureLevel() const {
928   if (!mDeviceStatus) {
929     return 0;
930   }
931   return mDeviceStatus->featureLevel();
932 }
933 
TextureSharingWorks()934 bool DeviceManagerDx::TextureSharingWorks() {
935   MutexAutoLock lock(mDeviceLock);
936   if (!mDeviceStatus) {
937     return false;
938   }
939   return mDeviceStatus->textureSharingWorks();
940 }
941 
CanInitializeKeyedMutexTextures()942 bool DeviceManagerDx::CanInitializeKeyedMutexTextures() {
943   MutexAutoLock lock(mDeviceLock);
944   return mDeviceStatus && gfxPrefs::Direct3D11AllowKeyedMutex() &&
945          gfxVars::AllowD3D11KeyedMutex();
946 }
947 
HasCrashyInitData()948 bool DeviceManagerDx::HasCrashyInitData() {
949   MutexAutoLock lock(mDeviceLock);
950   if (!mDeviceStatus) {
951     return false;
952   }
953 
954   return (mDeviceStatus->adapter().VendorId == 0x8086 && !IsWin10OrLater());
955 }
956 
CheckRemotePresentSupport()957 bool DeviceManagerDx::CheckRemotePresentSupport() {
958   MOZ_ASSERT(XRE_IsParentProcess());
959 
960   RefPtr<IDXGIAdapter1> adapter = GetDXGIAdapter();
961   if (!adapter) {
962     return false;
963   }
964   if (!D3D11Checks::DoesRemotePresentWork(adapter)) {
965     return false;
966   }
967   return true;
968 }
969 
IsWARP()970 bool DeviceManagerDx::IsWARP() {
971   MutexAutoLock lock(mDeviceLock);
972   if (!mDeviceStatus) {
973     return false;
974   }
975   return mDeviceStatus->isWARP();
976 }
977 
CanUseNV12()978 bool DeviceManagerDx::CanUseNV12() {
979   MutexAutoLock lock(mDeviceLock);
980   if (!mDeviceStatus) {
981     return false;
982   }
983   return mDeviceStatus->useNV12();
984 }
985 
InitializeDirectDraw()986 void DeviceManagerDx::InitializeDirectDraw() {
987   MOZ_ASSERT(layers::CompositorThreadHolder::IsInCompositorThread());
988 
989   if (mDirectDraw) {
990     // Already initialized.
991     return;
992   }
993 
994   FeatureState& ddraw = gfxConfig::GetFeature(Feature::DIRECT_DRAW);
995   if (!ddraw.IsEnabled()) {
996     return;
997   }
998 
999   // Check if DirectDraw is available on this system.
1000   mDirectDrawDLL.own(LoadLibrarySystem32(L"ddraw.dll"));
1001   if (!mDirectDrawDLL) {
1002     ddraw.SetFailed(FeatureStatus::Unavailable,
1003                     "DirectDraw not available on this computer",
1004                     NS_LITERAL_CSTRING("FEATURE_FAILURE_DDRAW_LIB"));
1005     return;
1006   }
1007 
1008   sDirectDrawCreateExFn = (decltype(DirectDrawCreateEx)*)GetProcAddress(
1009       mDirectDrawDLL, "DirectDrawCreateEx");
1010   if (!sDirectDrawCreateExFn) {
1011     ddraw.SetFailed(FeatureStatus::Unavailable,
1012                     "DirectDraw not available on this computer",
1013                     NS_LITERAL_CSTRING("FEATURE_FAILURE_DDRAW_LIB"));
1014     return;
1015   }
1016 
1017   HRESULT hr;
1018   MOZ_SEH_TRY {
1019     hr = sDirectDrawCreateExFn(nullptr, getter_AddRefs(mDirectDraw),
1020                                IID_IDirectDraw7, nullptr);
1021   }
1022   MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
1023     ddraw.SetFailed(FeatureStatus::Failed, "Failed to create DirectDraw",
1024                     NS_LITERAL_CSTRING("FEATURE_FAILURE_DDRAW_LIB"));
1025     gfxCriticalNote << "DoesCreatingDirectDrawFailed";
1026     return;
1027   }
1028   if (FAILED(hr)) {
1029     ddraw.SetFailed(FeatureStatus::Failed, "Failed to create DirectDraw",
1030                     NS_LITERAL_CSTRING("FEATURE_FAILURE_DDRAW_LIB"));
1031     gfxCriticalNote << "DoesCreatingDirectDrawFailed " << hexa(hr);
1032     return;
1033   }
1034 }
1035 
GetDirectDraw()1036 IDirectDraw7* DeviceManagerDx::GetDirectDraw() { return mDirectDraw; }
1037 
GetCompositorDevices(RefPtr<ID3D11Device> * aOutDevice,RefPtr<layers::DeviceAttachmentsD3D11> * aOutAttachments)1038 void DeviceManagerDx::GetCompositorDevices(
1039     RefPtr<ID3D11Device>* aOutDevice,
1040     RefPtr<layers::DeviceAttachmentsD3D11>* aOutAttachments) {
1041   MOZ_ASSERT(layers::CompositorThreadHolder::IsInCompositorThread());
1042 
1043   RefPtr<ID3D11Device> device;
1044   {
1045     MutexAutoLock lock(mDeviceLock);
1046     if (!mCompositorDevice) {
1047       return;
1048     }
1049     if (mCompositorAttachments) {
1050       *aOutDevice = mCompositorDevice;
1051       *aOutAttachments = mCompositorAttachments;
1052       return;
1053     }
1054 
1055     // Otherwise, we'll try to create attachments outside the lock.
1056     device = mCompositorDevice;
1057   }
1058 
1059   // We save the attachments object even if it fails to initialize, so the
1060   // compositor can grab the failure ID.
1061   RefPtr<layers::DeviceAttachmentsD3D11> attachments =
1062       layers::DeviceAttachmentsD3D11::Create(device);
1063   {
1064     MutexAutoLock lock(mDeviceLock);
1065     if (device != mCompositorDevice) {
1066       return;
1067     }
1068     mCompositorAttachments = attachments;
1069   }
1070 
1071   *aOutDevice = device;
1072   *aOutAttachments = attachments;
1073 }
1074 
PreloadAttachmentsOnCompositorThread()1075 /* static */ void DeviceManagerDx::PreloadAttachmentsOnCompositorThread() {
1076   MessageLoop* loop = layers::CompositorThreadHolder::Loop();
1077   if (!loop) {
1078     return;
1079   }
1080 
1081   bool enableAL = gfxConfig::IsEnabled(Feature::ADVANCED_LAYERS);
1082 
1083   RefPtr<Runnable> task = NS_NewRunnableFunction(
1084       "DeviceManagerDx::PreloadAttachmentsOnCompositorThread",
1085       [enableAL]() -> void {
1086         if (DeviceManagerDx* dm = DeviceManagerDx::Get()) {
1087           if (enableAL) {
1088             dm->GetMLGDevice();
1089           } else {
1090             RefPtr<ID3D11Device> device;
1091             RefPtr<layers::DeviceAttachmentsD3D11> attachments;
1092             dm->GetCompositorDevices(&device, &attachments);
1093           }
1094         }
1095       });
1096   loop->PostTask(task.forget());
1097 }
1098 
1099 }  // namespace gfx
1100 }  // namespace mozilla
1101