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