/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "D3D11Checks.h" #include "DXVA2Manager.h" #include "gfxConfig.h" #include "GfxDriverInfo.h" #include "gfxWindowsPlatform.h" #include "mozilla/Components.h" #include "mozilla/RefPtr.h" #include "mozilla/StaticPrefs_gfx.h" #include "mozilla/StaticPrefs_layers.h" #include "mozilla/StaticPrefs_media.h" #include "mozilla/gfx/gfxVars.h" #include "mozilla/gfx/Logging.h" #include "mozilla/layers/TextureD3D11.h" #include "nsIGfxInfo.h" #include #include #include #include #include namespace mozilla { namespace gfx { using namespace mozilla::widget; using mozilla::layers::AutoTextureLock; /* static */ bool D3D11Checks::DoesRenderTargetViewNeedRecreating(ID3D11Device* aDevice) { bool result = false; // CreateTexture2D is known to crash on lower feature levels, see bugs // 1170211 and 1089413. if (aDevice->GetFeatureLevel() < D3D_FEATURE_LEVEL_10_0) { return true; } RefPtr deviceContext; aDevice->GetImmediateContext(getter_AddRefs(deviceContext)); int backbufferWidth = 32; int backbufferHeight = 32; RefPtr offscreenTexture; RefPtr keyedMutex; D3D11_TEXTURE2D_DESC offscreenTextureDesc = {0}; offscreenTextureDesc.Width = backbufferWidth; offscreenTextureDesc.Height = backbufferHeight; offscreenTextureDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; offscreenTextureDesc.MipLevels = 0; offscreenTextureDesc.ArraySize = 1; offscreenTextureDesc.SampleDesc.Count = 1; offscreenTextureDesc.SampleDesc.Quality = 0; offscreenTextureDesc.Usage = D3D11_USAGE_DEFAULT; offscreenTextureDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; offscreenTextureDesc.CPUAccessFlags = 0; offscreenTextureDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_NTHANDLE | D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX; HRESULT hr = aDevice->CreateTexture2D(&offscreenTextureDesc, NULL, getter_AddRefs(offscreenTexture)); if (FAILED(hr)) { gfxCriticalNote << "DoesRecreatingCreateTexture2DFail"; return false; } hr = offscreenTexture->QueryInterface(__uuidof(IDXGIKeyedMutex), (void**)getter_AddRefs(keyedMutex)); if (FAILED(hr)) { gfxCriticalNote << "DoesRecreatingKeyedMutexFailed"; return false; } D3D11_RENDER_TARGET_VIEW_DESC offscreenRTVDesc; offscreenRTVDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; offscreenRTVDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; offscreenRTVDesc.Texture2D.MipSlice = 0; RefPtr offscreenRTView; hr = aDevice->CreateRenderTargetView(offscreenTexture, &offscreenRTVDesc, getter_AddRefs(offscreenRTView)); if (FAILED(hr)) { gfxCriticalNote << "DoesRecreatingCreateRenderTargetViewFailed"; return false; } { // Acquire and clear HRESULT hr; AutoTextureLock lock(keyedMutex, hr, INFINITE); FLOAT color1[4] = {1, 1, 0.5, 1}; deviceContext->ClearRenderTargetView(offscreenRTView, color1); } { HRESULT hr; AutoTextureLock lock(keyedMutex, hr, INFINITE); FLOAT color2[4] = {1, 1, 0, 1}; deviceContext->ClearRenderTargetView(offscreenRTView, color2); D3D11_TEXTURE2D_DESC desc; offscreenTexture->GetDesc(&desc); desc.Usage = D3D11_USAGE_STAGING; desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; desc.MiscFlags = 0; desc.BindFlags = 0; RefPtr cpuTexture; hr = aDevice->CreateTexture2D(&desc, NULL, getter_AddRefs(cpuTexture)); if (FAILED(hr)) { gfxCriticalNote << "DoesRecreatingCreateCPUTextureFailed"; return false; } deviceContext->CopyResource(cpuTexture, offscreenTexture); D3D11_MAPPED_SUBRESOURCE mapped; hr = deviceContext->Map(cpuTexture, 0, D3D11_MAP_READ, 0, &mapped); if (FAILED(hr)) { gfxCriticalNote << "DoesRecreatingMapFailed " << hexa(hr); return false; } uint32_t resultColor = *(uint32_t*)mapped.pData; deviceContext->Unmap(cpuTexture, 0); cpuTexture = nullptr; // XXX on some drivers resultColor will not have changed to // match the clear if (resultColor != 0xffffff00) { gfxCriticalNote << "RenderTargetViewNeedsRecreating"; result = true; } } return result; } /* static */ bool D3D11Checks::DoesDeviceWork() { static bool checked = false; static bool result = false; if (checked) return result; checked = true; if (StaticPrefs::gfx_direct2d_force_enabled_AtStartup() || gfxConfig::IsForcedOnByUser(Feature::HW_COMPOSITING)) { result = true; return true; } if (GetModuleHandleW(L"igd10umd32.dll")) { const wchar_t* checkModules[] = {L"dlumd32.dll", L"dlumd11.dll", L"dlumd10.dll"}; for (size_t i = 0; i < PR_ARRAY_SIZE(checkModules); i += 1) { if (GetModuleHandleW(checkModules[i])) { nsString displayLinkModuleVersionString; gfxWindowsPlatform::GetDLLVersion(checkModules[i], displayLinkModuleVersionString); uint64_t displayLinkModuleVersion; if (!ParseDriverVersion(displayLinkModuleVersionString, &displayLinkModuleVersion)) { gfxCriticalError() << "DisplayLink: could not parse version " << checkModules[i]; return false; } if (displayLinkModuleVersion <= V(8, 6, 1, 36484)) { NS_ConvertUTF16toUTF8 version(displayLinkModuleVersionString); gfxCriticalError(CriticalLog::DefaultOptions(false)) << "DisplayLink: too old version " << version.get(); return false; } } } } result = true; return true; } static bool TryCreateTexture2D(ID3D11Device* device, D3D11_TEXTURE2D_DESC* desc, D3D11_SUBRESOURCE_DATA* data, RefPtr& texture) { // Older Intel driver version (see bug 1221348 for version #s) crash when // creating a texture with shared keyed mutex and data. MOZ_SEH_TRY { return !FAILED( device->CreateTexture2D(desc, data, getter_AddRefs(texture))); } MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { // For now we want to aggregrate all the crash signature to a known crash. gfxDevCrash(LogReason::TextureCreation) << "Crash creating texture. See bug 1221348."; return false; } } // See bug 1083071. On some drivers, Direct3D 11 CreateShaderResourceView fails // with E_OUTOFMEMORY. static bool DoesTextureSharingWorkInternal(ID3D11Device* device, DXGI_FORMAT format, UINT bindflags) { // CreateTexture2D is known to crash on lower feature levels, see bugs // 1170211 and 1089413. if (device->GetFeatureLevel() < D3D_FEATURE_LEVEL_10_0) { return false; } if (StaticPrefs::gfx_direct2d_force_enabled_AtStartup() || gfxConfig::IsForcedOnByUser(Feature::HW_COMPOSITING)) { return true; } if (GetModuleHandleW(L"atidxx32.dll")) { nsCOMPtr gfxInfo = components::GfxInfo::Service(); if (gfxInfo) { nsString vendorID, vendorID2; gfxInfo->GetAdapterVendorID(vendorID); gfxInfo->GetAdapterVendorID2(vendorID2); if (vendorID.EqualsLiteral("0x8086") && vendorID2.IsEmpty()) { if (!StaticPrefs::layers_amd_switchable_gfx_enabled_AtStartup()) { return false; } gfxCriticalError(CriticalLog::DefaultOptions(false)) << "PossiblyBrokenSurfaceSharing_UnexpectedAMDGPU"; } } } RefPtr texture; D3D11_TEXTURE2D_DESC desc; const int texture_size = 32; desc.Width = texture_size; desc.Height = texture_size; desc.MipLevels = 1; desc.ArraySize = 1; desc.Format = format; desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; desc.Usage = D3D11_USAGE_DEFAULT; desc.CPUAccessFlags = 0; desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_NTHANDLE | D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX; desc.BindFlags = bindflags; uint32_t color[texture_size * texture_size]; for (size_t i = 0; i < sizeof(color) / sizeof(color[0]); i++) { color[i] = 0xff00ffff; } // XXX If we pass the data directly at texture creation time we // get a crash on Intel 8.5.10.[18xx-1994] drivers. // We can work around this issue by doing UpdateSubresource. if (!TryCreateTexture2D(device, &desc, nullptr, texture)) { gfxCriticalNote << "DoesD3D11TextureSharingWork_TryCreateTextureFailure"; return false; } RefPtr sourceSharedMutex; texture->QueryInterface(__uuidof(IDXGIKeyedMutex), (void**)getter_AddRefs(sourceSharedMutex)); if (FAILED(sourceSharedMutex->AcquireSync(0, 30 * 1000))) { gfxCriticalError() << "DoesD3D11TextureSharingWork_SourceMutexTimeout"; // only wait for 30 seconds return false; } RefPtr deviceContext; device->GetImmediateContext(getter_AddRefs(deviceContext)); int stride = texture_size * 4; deviceContext->UpdateSubresource(texture, 0, nullptr, color, stride, stride * texture_size); if (FAILED(sourceSharedMutex->ReleaseSync(0))) { gfxCriticalError() << "DoesD3D11TextureSharingWork_SourceReleaseSyncTimeout"; return false; } RefPtr otherResource; if (FAILED(texture->QueryInterface(__uuidof(IDXGIResource1), getter_AddRefs(otherResource)))) { gfxCriticalError() << "DoesD3D11TextureSharingWork_GetResourceFailure"; return false; } HANDLE sharedHandle; if (FAILED(otherResource->CreateSharedHandle( nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr, &sharedHandle))) { gfxCriticalError() << "DoesD3D11TextureSharingWork_GetSharedTextureFailure"; return false; } auto handle = ipc::FileDescriptor(UniqueFileHandle(sharedHandle)); RefPtr device1; device->QueryInterface((ID3D11Device1**)getter_AddRefs(device1)); if (!device1) { gfxCriticalNoteOnce << "Failed to get ID3D11Device1"; return false; } RefPtr sharedResource; RefPtr sharedTexture; auto raw = handle.TakePlatformHandle(); if (FAILED(device1->OpenSharedResource1(raw.get(), __uuidof(ID3D11Resource), getter_AddRefs(sharedResource)))) { gfxCriticalError(CriticalLog::DefaultOptions(false)) << "OpenSharedResource failed for format " << format; return false; } if (FAILED(sharedResource->QueryInterface(__uuidof(ID3D11Texture2D), getter_AddRefs(sharedTexture)))) { gfxCriticalError() << "DoesD3D11TextureSharingWork_GetSharedTextureFailure"; return false; } // create a staging texture for readback RefPtr cpuTexture; desc.Usage = D3D11_USAGE_STAGING; desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; desc.MiscFlags = 0; desc.BindFlags = 0; if (FAILED(device->CreateTexture2D(&desc, nullptr, getter_AddRefs(cpuTexture)))) { gfxCriticalError() << "DoesD3D11TextureSharingWork_CreateTextureFailure"; return false; } RefPtr sharedMutex; sharedResource->QueryInterface(__uuidof(IDXGIKeyedMutex), (void**)getter_AddRefs(sharedMutex)); { HRESULT hr; AutoTextureLock lock(sharedMutex, hr, 30 * 1000); if (FAILED(hr)) { gfxCriticalError() << "DoesD3D11TextureSharingWork_AcquireSyncTimeout"; // only wait for 30 seconds return false; } // Copy to the cpu texture so that we can readback deviceContext->CopyResource(cpuTexture, sharedTexture); // We only need to hold on to the mutex during the copy. sharedMutex->ReleaseSync(0); } D3D11_MAPPED_SUBRESOURCE mapped; uint32_t resultColor = 0; if (SUCCEEDED( deviceContext->Map(cpuTexture, 0, D3D11_MAP_READ, 0, &mapped))) { // read the texture resultColor = *(uint32_t*)mapped.pData; deviceContext->Unmap(cpuTexture, 0); } else { gfxCriticalError() << "DoesD3D11TextureSharingWork_MapFailed"; return false; } // check that the color we put in is the color we get out if (resultColor != color[0]) { // Shared surfaces seem to be broken on dual AMD & Intel HW when using the // AMD GPU gfxCriticalNote << "DoesD3D11TextureSharingWork_ColorMismatch"; return false; } RefPtr sharedView; // This if(FAILED()) is the one that actually fails on systems affected by bug // 1083071. if (FAILED(device->CreateShaderResourceView(sharedTexture, NULL, getter_AddRefs(sharedView)))) { gfxCriticalNote << "CreateShaderResourceView failed for format" << format; return false; } return true; } /* static */ bool D3D11Checks::DoesTextureSharingWork(ID3D11Device* device) { return DoesTextureSharingWorkInternal( device, DXGI_FORMAT_B8G8R8A8_UNORM, D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE); } /* static */ bool D3D11Checks::DoesAlphaTextureSharingWork(ID3D11Device* device) { return DoesTextureSharingWorkInternal(device, DXGI_FORMAT_R8_UNORM, D3D11_BIND_SHADER_RESOURCE); } /* static */ bool D3D11Checks::GetDxgiDesc(ID3D11Device* device, DXGI_ADAPTER_DESC* out) { RefPtr dxgiDevice; HRESULT hr = device->QueryInterface(__uuidof(IDXGIDevice), getter_AddRefs(dxgiDevice)); if (FAILED(hr)) { return false; } RefPtr dxgiAdapter; if (FAILED(dxgiDevice->GetAdapter(getter_AddRefs(dxgiAdapter)))) { return false; } return SUCCEEDED(dxgiAdapter->GetDesc(out)); } /* static */ void D3D11Checks::WarnOnAdapterMismatch(ID3D11Device* device) { DXGI_ADAPTER_DESC desc; PodZero(&desc); GetDxgiDesc(device, &desc); nsCOMPtr gfxInfo = components::GfxInfo::Service(); nsString vendorID; gfxInfo->GetAdapterVendorID(vendorID); nsresult ec; int32_t vendor = vendorID.ToInteger(&ec, 16); if (vendor != static_cast(desc.VendorId)) { gfxCriticalNote << "VendorIDMismatch V " << hexa(vendor) << " " << hexa(desc.VendorId); } } /* static */ bool D3D11Checks::DoesRemotePresentWork(IDXGIAdapter* adapter) { // Remote presentation was added in DXGI 1.2, for Windows 8 and the Platform // Update to Windows 7. RefPtr check; HRESULT hr = adapter->QueryInterface(__uuidof(IDXGIAdapter2), getter_AddRefs(check)); return SUCCEEDED(hr) && check; } /* static */ D3D11Checks::VideoFormatOptionSet D3D11Checks::FormatOptions( ID3D11Device* device) { auto doesNV12Work = [&]() { if (gfxVars::DXNV12Blocked()) { return false; } DXGI_ADAPTER_DESC desc; PodZero(&desc); if (!GetDxgiDesc(device, &desc)) { // Failed to retrieve device information, assume it doesn't work return false; } UINT formatSupport; HRESULT hr = device->CheckFormatSupport(DXGI_FORMAT_NV12, &formatSupport); if (FAILED(hr) || !(formatSupport & D3D11_FORMAT_SUPPORT_TEXTURE2D)) { return false; } nsString version; nsCOMPtr gfxInfo = components::GfxInfo::Service(); if (gfxInfo) { gfxInfo->GetAdapterDriverVersion(version); } return DXVA2Manager::IsNV12Supported(desc.VendorId, desc.DeviceId, version); }; auto doesP010Work = [&]() { if (gfxVars::DXP010Blocked() && !StaticPrefs::media_wmf_force_allow_p010_format()) { return false; } UINT formatSupport; HRESULT hr = device->CheckFormatSupport(DXGI_FORMAT_P010, &formatSupport); return (SUCCEEDED(hr) && (formatSupport & D3D11_FORMAT_SUPPORT_TEXTURE2D)); }; auto doesP016Work = [&]() { if (gfxVars::DXP016Blocked() && !StaticPrefs::media_wmf_force_allow_p010_format()) { return false; } UINT formatSupport; HRESULT hr = device->CheckFormatSupport(DXGI_FORMAT_P016, &formatSupport); return (SUCCEEDED(hr) && (formatSupport & D3D11_FORMAT_SUPPORT_TEXTURE2D)); }; VideoFormatOptionSet options; if (!doesNV12Work()) { // If the device doesn't support NV12, there's really no point testing for // P010 and P016. return options; } options += VideoFormatOption::NV12; if (doesP010Work()) { options += VideoFormatOption::P010; } if (doesP016Work()) { options += VideoFormatOption::P016; } return options; } } // namespace gfx } // namespace mozilla