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