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