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