1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
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 "D3D11Checks.h"
7 #include "DXVA2Manager.h"
8 #include "gfxConfig.h"
9 #include "GfxDriverInfo.h"
10 #include "gfxWindowsPlatform.h"
11 #include "mozilla/Components.h"
12 #include "mozilla/RefPtr.h"
13 #include "mozilla/StaticPrefs_gfx.h"
14 #include "mozilla/StaticPrefs_layers.h"
15 #include "mozilla/StaticPrefs_media.h"
16 #include "mozilla/gfx/gfxVars.h"
17 #include "mozilla/gfx/Logging.h"
18 #include "mozilla/layers/TextureD3D11.h"
19 #include "nsIGfxInfo.h"
20 #include <dxgi.h>
21 #include <dxgi1_2.h>
22 #include <d3d10_1.h>
23 #include <d3d11.h>
24 
25 namespace mozilla {
26 namespace gfx {
27 
28 using namespace mozilla::widget;
29 using mozilla::layers::AutoTextureLock;
30 
31 /* static */
DoesRenderTargetViewNeedRecreating(ID3D11Device * aDevice)32 bool D3D11Checks::DoesRenderTargetViewNeedRecreating(ID3D11Device* aDevice) {
33   bool result = false;
34   // CreateTexture2D is known to crash on lower feature levels, see bugs
35   // 1170211 and 1089413.
36   if (aDevice->GetFeatureLevel() < D3D_FEATURE_LEVEL_10_0) {
37     return true;
38   }
39 
40   RefPtr<ID3D11DeviceContext> deviceContext;
41   aDevice->GetImmediateContext(getter_AddRefs(deviceContext));
42   int backbufferWidth = 32;
43   int backbufferHeight = 32;
44   RefPtr<ID3D11Texture2D> offscreenTexture;
45   RefPtr<IDXGIKeyedMutex> keyedMutex;
46 
47   D3D11_TEXTURE2D_DESC offscreenTextureDesc = {0};
48   offscreenTextureDesc.Width = backbufferWidth;
49   offscreenTextureDesc.Height = backbufferHeight;
50   offscreenTextureDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
51   offscreenTextureDesc.MipLevels = 0;
52   offscreenTextureDesc.ArraySize = 1;
53   offscreenTextureDesc.SampleDesc.Count = 1;
54   offscreenTextureDesc.SampleDesc.Quality = 0;
55   offscreenTextureDesc.Usage = D3D11_USAGE_DEFAULT;
56   offscreenTextureDesc.BindFlags =
57       D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
58   offscreenTextureDesc.CPUAccessFlags = 0;
59   offscreenTextureDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
60 
61   HRESULT hr = aDevice->CreateTexture2D(&offscreenTextureDesc, NULL,
62                                         getter_AddRefs(offscreenTexture));
63   if (FAILED(hr)) {
64     gfxCriticalNote << "DoesRecreatingCreateTexture2DFail";
65     return false;
66   }
67 
68   hr = offscreenTexture->QueryInterface(__uuidof(IDXGIKeyedMutex),
69                                         (void**)getter_AddRefs(keyedMutex));
70   if (FAILED(hr)) {
71     gfxCriticalNote << "DoesRecreatingKeyedMutexFailed";
72     return false;
73   }
74   D3D11_RENDER_TARGET_VIEW_DESC offscreenRTVDesc;
75   offscreenRTVDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
76   offscreenRTVDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
77   offscreenRTVDesc.Texture2D.MipSlice = 0;
78 
79   RefPtr<ID3D11RenderTargetView> offscreenRTView;
80   hr = aDevice->CreateRenderTargetView(offscreenTexture, &offscreenRTVDesc,
81                                        getter_AddRefs(offscreenRTView));
82   if (FAILED(hr)) {
83     gfxCriticalNote << "DoesRecreatingCreateRenderTargetViewFailed";
84     return false;
85   }
86 
87   {
88     // Acquire and clear
89     HRESULT hr;
90     AutoTextureLock lock(keyedMutex, hr, INFINITE);
91     FLOAT color1[4] = {1, 1, 0.5, 1};
92     deviceContext->ClearRenderTargetView(offscreenRTView, color1);
93   }
94 
95   {
96     HRESULT hr;
97     AutoTextureLock lock(keyedMutex, hr, INFINITE);
98     FLOAT color2[4] = {1, 1, 0, 1};
99 
100     deviceContext->ClearRenderTargetView(offscreenRTView, color2);
101     D3D11_TEXTURE2D_DESC desc;
102 
103     offscreenTexture->GetDesc(&desc);
104     desc.Usage = D3D11_USAGE_STAGING;
105     desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
106     desc.MiscFlags = 0;
107     desc.BindFlags = 0;
108     RefPtr<ID3D11Texture2D> cpuTexture;
109     hr = aDevice->CreateTexture2D(&desc, NULL, getter_AddRefs(cpuTexture));
110     if (FAILED(hr)) {
111       gfxCriticalNote << "DoesRecreatingCreateCPUTextureFailed";
112       return false;
113     }
114 
115     deviceContext->CopyResource(cpuTexture, offscreenTexture);
116 
117     D3D11_MAPPED_SUBRESOURCE mapped;
118     hr = deviceContext->Map(cpuTexture, 0, D3D11_MAP_READ, 0, &mapped);
119     if (FAILED(hr)) {
120       gfxCriticalNote << "DoesRecreatingMapFailed " << hexa(hr);
121       return false;
122     }
123     int resultColor = *(int*)mapped.pData;
124     deviceContext->Unmap(cpuTexture, 0);
125     cpuTexture = nullptr;
126 
127     // XXX on some drivers resultColor will not have changed to
128     // match the clear
129     if (resultColor != 0xffffff00) {
130       gfxCriticalNote << "RenderTargetViewNeedsRecreating";
131       result = true;
132     }
133   }
134   return result;
135 }
136 
137 /* static */
DoesDeviceWork()138 bool D3D11Checks::DoesDeviceWork() {
139   static bool checked = false;
140   static bool result = false;
141 
142   if (checked) return result;
143   checked = true;
144 
145   if (StaticPrefs::gfx_direct2d_force_enabled_AtStartup() ||
146       gfxConfig::IsForcedOnByUser(Feature::HW_COMPOSITING)) {
147     result = true;
148     return true;
149   }
150 
151   if (GetModuleHandleW(L"igd10umd32.dll")) {
152     const wchar_t* checkModules[] = {L"dlumd32.dll", L"dlumd11.dll",
153                                      L"dlumd10.dll"};
154     for (int i = 0; i < PR_ARRAY_SIZE(checkModules); i += 1) {
155       if (GetModuleHandleW(checkModules[i])) {
156         nsString displayLinkModuleVersionString;
157         gfxWindowsPlatform::GetDLLVersion(checkModules[i],
158                                           displayLinkModuleVersionString);
159         uint64_t displayLinkModuleVersion;
160         if (!ParseDriverVersion(displayLinkModuleVersionString,
161                                 &displayLinkModuleVersion)) {
162           gfxCriticalError()
163               << "DisplayLink: could not parse version " << checkModules[i];
164           return false;
165         }
166         if (displayLinkModuleVersion <= V(8, 6, 1, 36484)) {
167           NS_ConvertUTF16toUTF8 version(displayLinkModuleVersionString);
168           gfxCriticalError(CriticalLog::DefaultOptions(false))
169               << "DisplayLink: too old version " << version.get();
170           return false;
171         }
172       }
173     }
174   }
175   result = true;
176   return true;
177 }
178 
TryCreateTexture2D(ID3D11Device * device,D3D11_TEXTURE2D_DESC * desc,D3D11_SUBRESOURCE_DATA * data,RefPtr<ID3D11Texture2D> & texture)179 static bool TryCreateTexture2D(ID3D11Device* device, D3D11_TEXTURE2D_DESC* desc,
180                                D3D11_SUBRESOURCE_DATA* data,
181                                RefPtr<ID3D11Texture2D>& texture) {
182   // Older Intel driver version (see bug 1221348 for version #s) crash when
183   // creating a texture with shared keyed mutex and data.
184   MOZ_SEH_TRY {
185     return !FAILED(
186         device->CreateTexture2D(desc, data, getter_AddRefs(texture)));
187   }
188   MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
189     // For now we want to aggregrate all the crash signature to a known crash.
190     gfxDevCrash(LogReason::TextureCreation)
191         << "Crash creating texture. See bug 1221348.";
192     return false;
193   }
194 }
195 
196 // See bug 1083071. On some drivers, Direct3D 11 CreateShaderResourceView fails
197 // with E_OUTOFMEMORY.
DoesTextureSharingWorkInternal(ID3D11Device * device,DXGI_FORMAT format,UINT bindflags)198 static bool DoesTextureSharingWorkInternal(ID3D11Device* device,
199                                            DXGI_FORMAT format, UINT bindflags) {
200   // CreateTexture2D is known to crash on lower feature levels, see bugs
201   // 1170211 and 1089413.
202   if (device->GetFeatureLevel() < D3D_FEATURE_LEVEL_10_0) {
203     return false;
204   }
205 
206   if (StaticPrefs::gfx_direct2d_force_enabled_AtStartup() ||
207       gfxConfig::IsForcedOnByUser(Feature::HW_COMPOSITING)) {
208     return true;
209   }
210 
211   if (GetModuleHandleW(L"atidxx32.dll")) {
212     nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service();
213     if (gfxInfo) {
214       nsString vendorID, vendorID2;
215       gfxInfo->GetAdapterVendorID(vendorID);
216       gfxInfo->GetAdapterVendorID2(vendorID2);
217       if (vendorID.EqualsLiteral("0x8086") && vendorID2.IsEmpty()) {
218         if (!StaticPrefs::layers_amd_switchable_gfx_enabled_AtStartup()) {
219           return false;
220         }
221         gfxCriticalError(CriticalLog::DefaultOptions(false))
222             << "PossiblyBrokenSurfaceSharing_UnexpectedAMDGPU";
223       }
224     }
225   }
226 
227   RefPtr<ID3D11Texture2D> texture;
228   D3D11_TEXTURE2D_DESC desc;
229   const int texture_size = 32;
230   desc.Width = texture_size;
231   desc.Height = texture_size;
232   desc.MipLevels = 1;
233   desc.ArraySize = 1;
234   desc.Format = format;
235   desc.SampleDesc.Count = 1;
236   desc.SampleDesc.Quality = 0;
237   desc.Usage = D3D11_USAGE_DEFAULT;
238   desc.CPUAccessFlags = 0;
239   desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
240   desc.BindFlags = bindflags;
241 
242   uint32_t color[texture_size * texture_size];
243   for (size_t i = 0; i < sizeof(color) / sizeof(color[0]); i++) {
244     color[i] = 0xff00ffff;
245   }
246   // XXX If we pass the data directly at texture creation time we
247   //     get a crash on Intel 8.5.10.[18xx-1994] drivers.
248   //     We can work around this issue by doing UpdateSubresource.
249   if (!TryCreateTexture2D(device, &desc, nullptr, texture)) {
250     gfxCriticalNote << "DoesD3D11TextureSharingWork_TryCreateTextureFailure";
251     return false;
252   }
253 
254   RefPtr<IDXGIKeyedMutex> sourceSharedMutex;
255   texture->QueryInterface(__uuidof(IDXGIKeyedMutex),
256                           (void**)getter_AddRefs(sourceSharedMutex));
257   if (FAILED(sourceSharedMutex->AcquireSync(0, 30 * 1000))) {
258     gfxCriticalError() << "DoesD3D11TextureSharingWork_SourceMutexTimeout";
259     // only wait for 30 seconds
260     return false;
261   }
262 
263   RefPtr<ID3D11DeviceContext> deviceContext;
264   device->GetImmediateContext(getter_AddRefs(deviceContext));
265 
266   int stride = texture_size * 4;
267   deviceContext->UpdateSubresource(texture, 0, nullptr, color, stride,
268                                    stride * texture_size);
269 
270   if (FAILED(sourceSharedMutex->ReleaseSync(0))) {
271     gfxCriticalError()
272         << "DoesD3D11TextureSharingWork_SourceReleaseSyncTimeout";
273     return false;
274   }
275 
276   HANDLE shareHandle;
277   RefPtr<IDXGIResource> otherResource;
278   if (FAILED(texture->QueryInterface(__uuidof(IDXGIResource),
279                                      getter_AddRefs(otherResource)))) {
280     gfxCriticalError() << "DoesD3D11TextureSharingWork_GetResourceFailure";
281     return false;
282   }
283 
284   if (FAILED(otherResource->GetSharedHandle(&shareHandle))) {
285     gfxCriticalError() << "DoesD3D11TextureSharingWork_GetSharedTextureFailure";
286     return false;
287   }
288 
289   RefPtr<ID3D11Resource> sharedResource;
290   RefPtr<ID3D11Texture2D> sharedTexture;
291   if (FAILED(device->OpenSharedResource(shareHandle, __uuidof(ID3D11Resource),
292                                         getter_AddRefs(sharedResource)))) {
293     gfxCriticalError(CriticalLog::DefaultOptions(false))
294         << "OpenSharedResource failed for format " << format;
295     return false;
296   }
297 
298   if (FAILED(sharedResource->QueryInterface(__uuidof(ID3D11Texture2D),
299                                             getter_AddRefs(sharedTexture)))) {
300     gfxCriticalError() << "DoesD3D11TextureSharingWork_GetSharedTextureFailure";
301     return false;
302   }
303 
304   // create a staging texture for readback
305   RefPtr<ID3D11Texture2D> cpuTexture;
306   desc.Usage = D3D11_USAGE_STAGING;
307   desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
308   desc.MiscFlags = 0;
309   desc.BindFlags = 0;
310   if (FAILED(device->CreateTexture2D(&desc, nullptr,
311                                      getter_AddRefs(cpuTexture)))) {
312     gfxCriticalError() << "DoesD3D11TextureSharingWork_CreateTextureFailure";
313     return false;
314   }
315 
316   RefPtr<IDXGIKeyedMutex> sharedMutex;
317   sharedResource->QueryInterface(__uuidof(IDXGIKeyedMutex),
318                                  (void**)getter_AddRefs(sharedMutex));
319   {
320     HRESULT hr;
321     AutoTextureLock lock(sharedMutex, hr, 30 * 1000);
322     if (FAILED(hr)) {
323       gfxCriticalError() << "DoesD3D11TextureSharingWork_AcquireSyncTimeout";
324       // only wait for 30 seconds
325       return false;
326     }
327 
328     // Copy to the cpu texture so that we can readback
329     deviceContext->CopyResource(cpuTexture, sharedTexture);
330 
331     // We only need to hold on to the mutex during the copy.
332     sharedMutex->ReleaseSync(0);
333   }
334 
335   D3D11_MAPPED_SUBRESOURCE mapped;
336   uint32_t resultColor = 0;
337   if (SUCCEEDED(
338           deviceContext->Map(cpuTexture, 0, D3D11_MAP_READ, 0, &mapped))) {
339     // read the texture
340     resultColor = *(uint32_t*)mapped.pData;
341     deviceContext->Unmap(cpuTexture, 0);
342   } else {
343     gfxCriticalError() << "DoesD3D11TextureSharingWork_MapFailed";
344     return false;
345   }
346 
347   // check that the color we put in is the color we get out
348   if (resultColor != color[0]) {
349     // Shared surfaces seem to be broken on dual AMD & Intel HW when using the
350     // AMD GPU
351     gfxCriticalNote << "DoesD3D11TextureSharingWork_ColorMismatch";
352     return false;
353   }
354 
355   RefPtr<ID3D11ShaderResourceView> sharedView;
356 
357   // This if(FAILED()) is the one that actually fails on systems affected by bug
358   // 1083071.
359   if (FAILED(device->CreateShaderResourceView(sharedTexture, NULL,
360                                               getter_AddRefs(sharedView)))) {
361     gfxCriticalNote << "CreateShaderResourceView failed for format" << format;
362     return false;
363   }
364 
365   return true;
366 }
367 
368 /* static */
DoesTextureSharingWork(ID3D11Device * device)369 bool D3D11Checks::DoesTextureSharingWork(ID3D11Device* device) {
370   return DoesTextureSharingWorkInternal(
371       device, DXGI_FORMAT_B8G8R8A8_UNORM,
372       D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE);
373 }
374 
375 /* static */
DoesAlphaTextureSharingWork(ID3D11Device * device)376 bool D3D11Checks::DoesAlphaTextureSharingWork(ID3D11Device* device) {
377   return DoesTextureSharingWorkInternal(device, DXGI_FORMAT_R8_UNORM,
378                                         D3D11_BIND_SHADER_RESOURCE);
379 }
380 
381 /* static */
GetDxgiDesc(ID3D11Device * device,DXGI_ADAPTER_DESC * out)382 bool D3D11Checks::GetDxgiDesc(ID3D11Device* device, DXGI_ADAPTER_DESC* out) {
383   RefPtr<IDXGIDevice> dxgiDevice;
384   HRESULT hr =
385       device->QueryInterface(__uuidof(IDXGIDevice), getter_AddRefs(dxgiDevice));
386   if (FAILED(hr)) {
387     return false;
388   }
389 
390   RefPtr<IDXGIAdapter> dxgiAdapter;
391   if (FAILED(dxgiDevice->GetAdapter(getter_AddRefs(dxgiAdapter)))) {
392     return false;
393   }
394 
395   return SUCCEEDED(dxgiAdapter->GetDesc(out));
396 }
397 
398 /* static */
WarnOnAdapterMismatch(ID3D11Device * device)399 void D3D11Checks::WarnOnAdapterMismatch(ID3D11Device* device) {
400   DXGI_ADAPTER_DESC desc;
401   PodZero(&desc);
402   GetDxgiDesc(device, &desc);
403 
404   nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service();
405   nsString vendorID;
406   gfxInfo->GetAdapterVendorID(vendorID);
407   nsresult ec;
408   int32_t vendor = vendorID.ToInteger(&ec, 16);
409   if (vendor != desc.VendorId) {
410     gfxCriticalNote << "VendorIDMismatch V " << hexa(vendor) << " "
411                     << hexa(desc.VendorId);
412   }
413 }
414 
415 /* static */
DoesRemotePresentWork(IDXGIAdapter * adapter)416 bool D3D11Checks::DoesRemotePresentWork(IDXGIAdapter* adapter) {
417   // Remote presentation was added in DXGI 1.2, for Windows 8 and the Platform
418   // Update to Windows 7.
419   RefPtr<IDXGIAdapter2> check;
420   HRESULT hr =
421       adapter->QueryInterface(__uuidof(IDXGIAdapter2), getter_AddRefs(check));
422   return SUCCEEDED(hr) && check;
423 }
424 
FormatOptions(ID3D11Device * device)425 /* static */ D3D11Checks::VideoFormatOptionSet D3D11Checks::FormatOptions(
426     ID3D11Device* device) {
427   auto doesNV12Work = [&]() {
428     if (gfxVars::DXNV12Blocked()) {
429       return false;
430     }
431 
432     DXGI_ADAPTER_DESC desc;
433     PodZero(&desc);
434     if (!GetDxgiDesc(device, &desc)) {
435       // Failed to retrieve device information, assume it doesn't work
436       return false;
437     }
438 
439     UINT formatSupport;
440     HRESULT hr = device->CheckFormatSupport(DXGI_FORMAT_NV12, &formatSupport);
441     if (FAILED(hr) || !(formatSupport & D3D11_FORMAT_SUPPORT_TEXTURE2D)) {
442       return false;
443     }
444 
445     nsString version;
446     nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service();
447     if (gfxInfo) {
448       gfxInfo->GetAdapterDriverVersion(version);
449     }
450     return DXVA2Manager::IsNV12Supported(desc.VendorId, desc.DeviceId, version);
451   };
452 
453   auto doesP010Work = [&]() {
454     if (gfxVars::DXP010Blocked() &&
455         !StaticPrefs::media_wmf_force_allow_p010_format()) {
456       return false;
457     }
458     UINT formatSupport;
459     HRESULT hr = device->CheckFormatSupport(DXGI_FORMAT_P010, &formatSupport);
460     return (SUCCEEDED(hr) && (formatSupport & D3D11_FORMAT_SUPPORT_TEXTURE2D));
461   };
462 
463   auto doesP016Work = [&]() {
464     if (gfxVars::DXP016Blocked() &&
465         !StaticPrefs::media_wmf_force_allow_p010_format()) {
466       return false;
467     }
468     UINT formatSupport;
469     HRESULT hr = device->CheckFormatSupport(DXGI_FORMAT_P016, &formatSupport);
470     return (SUCCEEDED(hr) && (formatSupport & D3D11_FORMAT_SUPPORT_TEXTURE2D));
471   };
472 
473   VideoFormatOptionSet options;
474   if (!doesNV12Work()) {
475     // If the device doesn't support NV12, there's really no point testing for
476     // P010 and P016.
477     return options;
478   }
479   options += VideoFormatOption::NV12;
480   if (doesP010Work()) {
481     options += VideoFormatOption::P010;
482   }
483   if (doesP016Work()) {
484     options += VideoFormatOption::P016;
485   }
486   return options;
487 }
488 
489 }  // namespace gfx
490 }  // namespace mozilla
491