diff options
Diffstat (limited to 'gfx/thebes/D3D11Checks.cpp')
-rw-r--r-- | gfx/thebes/D3D11Checks.cpp | 505 |
1 files changed, 505 insertions, 0 deletions
diff --git a/gfx/thebes/D3D11Checks.cpp b/gfx/thebes/D3D11Checks.cpp new file mode 100644 index 0000000000..72af5a3ed2 --- /dev/null +++ b/gfx/thebes/D3D11Checks.cpp @@ -0,0 +1,505 @@ +/* -*- 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 <dxgi.h> +#include <dxgi1_2.h> +#include <d3d10_1.h> +#include <d3d11.h> +#include <d3d11_1.h> + +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<ID3D11DeviceContext> deviceContext; + aDevice->GetImmediateContext(getter_AddRefs(deviceContext)); + int backbufferWidth = 32; + int backbufferHeight = 32; + RefPtr<ID3D11Texture2D> offscreenTexture; + RefPtr<IDXGIKeyedMutex> 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<ID3D11RenderTargetView> 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<ID3D11Texture2D> 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<ID3D11Texture2D>& 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<nsIGfxInfo> 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<ID3D11Texture2D> 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<IDXGIKeyedMutex> 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<ID3D11DeviceContext> 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<IDXGIResource1> 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<ID3D11Device1> device1; + device->QueryInterface((ID3D11Device1**)getter_AddRefs(device1)); + if (!device1) { + gfxCriticalNoteOnce << "Failed to get ID3D11Device1"; + return false; + } + + RefPtr<ID3D11Resource> sharedResource; + RefPtr<ID3D11Texture2D> 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<ID3D11Texture2D> 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<IDXGIKeyedMutex> 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<ID3D11ShaderResourceView> 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<IDXGIDevice> dxgiDevice; + HRESULT hr = + device->QueryInterface(__uuidof(IDXGIDevice), getter_AddRefs(dxgiDevice)); + if (FAILED(hr)) { + return false; + } + + RefPtr<IDXGIAdapter> 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<nsIGfxInfo> gfxInfo = components::GfxInfo::Service(); + nsString vendorID; + gfxInfo->GetAdapterVendorID(vendorID); + nsresult ec; + int32_t vendor = vendorID.ToInteger(&ec, 16); + if (vendor != static_cast<int32_t>(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<IDXGIAdapter2> 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<nsIGfxInfo> 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 |