From a10ad42d907e4eabddd669ab968c6f6336b36581 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Tue, 28 Jul 2015 16:52:54 -0700 Subject: [PATCH] Refactor graphics device initialization on Windows. (bug 1183910 part 1, r=mattwoodrow,bas) This patch addresses a number of inconsistencies in the device initialization process, as well as simplifying it for future use. All device decisions are now explicitly made up-front during startup, rather than implicitly or on-demand. In addition a number of restrictions have been placed on when we can construct devices. Full change list: (1) We no longer attempt to use D3D11 if acceleration is disabled or D3D9 is preferred. This is a departure from our previous behavior, where we would construct these devices but then not use them as a compositor backend. (2) D3D11 startup no longer creates a content device (this is reserved for D2D initialization). (3) D2D is only attempted if we managed to create a D3D11 compositor device. This is a departure from previous behavior where if D3D11 was not used for compositing, we could still create its machinery to use D2D as a content backend. (4) D2D 1.1 initialization is now directly responsible for creating a D3D11 content device. (5) D2D 1.0 and 1.1 logic have been disentangled for clarity. (6) UpdateRenderMode() has been split up, so we can update backend prefs out of band with device resets. (7) mUseGDIFonts and mUseDirectWrite have been removed as their state was confusing. Instead, D2D now depends on DWrite initialization succeeding. If later we fail to get a DWrite font list, we revert our decision to use Direct2D. (8) Device resets now clear a little more state, including the devices set in Moz2D Factory. (9) We no longer create a DWrite text analyzer as it was unused. --- gfx/2d/Factory.cpp | 4 +- gfx/layers/client/TextureClient.cpp | 8 +- gfx/thebes/gfxPlatform.cpp | 2 +- gfx/thebes/gfxWindowsPlatform.cpp | 586 +++++++++++++++------------- gfx/thebes/gfxWindowsPlatform.h | 55 +-- 5 files changed, 361 insertions(+), 294 deletions(-) diff --git a/gfx/2d/Factory.cpp b/gfx/2d/Factory.cpp index bb6fab7d195..76ae17e2e29 100644 --- a/gfx/2d/Factory.cpp +++ b/gfx/2d/Factory.cpp @@ -621,7 +621,9 @@ Factory::SetDirect3D10Device(ID3D10Device1 *aDevice) // do not throw on failure; return error codes and disconnect the device // On Windows 8 error codes are the default, but on Windows 7 the // default is to throw (or perhaps only with some drivers?) - aDevice->SetExceptionMode(0); + if (aDevice) { + aDevice->SetExceptionMode(0); + } mD3D10Device = aDevice; } diff --git a/gfx/layers/client/TextureClient.cpp b/gfx/layers/client/TextureClient.cpp index afa4f53af9a..e0f6334d8f8 100644 --- a/gfx/layers/client/TextureClient.cpp +++ b/gfx/layers/client/TextureClient.cpp @@ -342,11 +342,11 @@ TextureClient::CreateForDrawing(ISurfaceAllocator* aAllocator, #ifdef XP_WIN LayersBackend parentBackend = aAllocator->GetCompositorBackendType(); if (parentBackend == LayersBackend::LAYERS_D3D11 && - (aMoz2DBackend == gfx::BackendType::DIRECT2D || - aMoz2DBackend == gfx::BackendType::DIRECT2D1_1) && - gfxWindowsPlatform::GetPlatform()->GetD3D10Device() && + ((aMoz2DBackend == gfx::BackendType::DIRECT2D && Factory::GetDirect3D10Device()) || + (aMoz2DBackend == gfx::BackendType::DIRECT2D1_1 && Factory::GetDirect3D11Device())) && aSize.width <= maxTextureSize && - aSize.height <= maxTextureSize) { + aSize.height <= maxTextureSize) + { texture = new TextureClientD3D11(aAllocator, aFormat, aTextureFlags); } if (parentBackend == LayersBackend::LAYERS_D3D9 && diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index 041e90d77cf..8a986768a5a 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -2237,7 +2237,7 @@ gfxPlatform::ShouldUseLayersAcceleration() if (gfxPrefs::LayersAccelerationForceEnabled()) { return true; } - if (gfxPlatform::GetPlatform()->AccelerateLayersByDefault()) { + if (AccelerateLayersByDefault()) { return true; } if (acceleratedEnv && *acceleratedEnv != '0') { diff --git a/gfx/thebes/gfxWindowsPlatform.cpp b/gfx/thebes/gfxWindowsPlatform.cpp index 1a2a4c8f01b..79c5fcb34b5 100755 --- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -373,18 +373,18 @@ public: NS_IMPL_ISUPPORTS(D3D9SharedTextureReporter, nsIMemoryReporter) gfxWindowsPlatform::gfxWindowsPlatform() - : mD3D11DeviceInitialized(false) + : mRenderMode(RENDER_GDI) , mIsWARP(false) , mHasDeviceReset(false) , mDoesD3D11TextureSharingWork(false) + , mAcceleration(FeatureStatus::Unused) , mD3D11Status(FeatureStatus::Unused) , mD2DStatus(FeatureStatus::Unused) + , mD2D1Status(FeatureStatus::Unused) { mUseClearTypeForDownloadableFonts = UNINITIALIZED_VALUE; mUseClearTypeAlways = UNINITIALIZED_VALUE; - mUsingGDIFonts = false; - /* * Initialize COM */ @@ -392,10 +392,16 @@ gfxWindowsPlatform::gfxWindowsPlatform() RegisterStrongMemoryReporter(new GfxD2DVramReporter()); - if (gfxPrefs::Direct2DUse1_1()) { - InitD3D11Devices(); + // Set up the D3D11 feature levels we can ask for. + if (IsWin8OrLater()) { + mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_11_1); } + mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_11_0); + mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_10_1); + mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_10_0); + mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_9_3); + InitializeDevices(); UpdateRenderMode(); RegisterStrongMemoryReporter(new GPUAdapterReporter()); @@ -438,161 +444,106 @@ gfxWindowsPlatform::CanUseHardwareVideoDecoding() return !IsWARP() && gfxPlatform::CanUseHardwareVideoDecoding(); } -FeatureStatus -gfxWindowsPlatform::InitD2DSupport() -{ -#ifdef CAIRO_HAS_D2D_SURFACE - bool d2dBlocked = false; - nsCOMPtr gfxInfo = services::GetGfxInfo(); - if (gfxInfo) { - int32_t status; - if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT2D, &status))) { - if (status != nsIGfxInfo::FEATURE_STATUS_OK) { - d2dBlocked = true; - } - } - if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, &status))) { - if (status != nsIGfxInfo::FEATURE_STATUS_OK) { - d2dBlocked = true; - } - } - } - - // If D2D is blocked or D3D9 is prefered, and D2D is not force-enabled, then - // we don't attempt to use D2D. - if (!gfxPrefs::Direct2DForceEnabled()) { - if (d2dBlocked) { - return FeatureStatus::Blacklisted; - } - if (gfxPrefs::LayersPreferD3D9()) { - return FeatureStatus::Disabled; - } - } - - // Do not ever try to use D2D if it's explicitly disabled or if we're not - // using DWrite fonts. - if (gfxPrefs::Direct2DDisabled() || mUsingGDIFonts) { - return FeatureStatus::Disabled; - } - - if (!IsVistaOrLater() || !GetD3D11Device()) { - return FeatureStatus::Unavailable; - } - if (!mDoesD3D11TextureSharingWork) { - return FeatureStatus::Failed; - } - if (InSafeMode()) { - return FeatureStatus::Blocked; - } - - VerifyD2DDevice(gfxPrefs::Direct2DForceEnabled()); - if (!mD3D10Device || !GetD3D11Device()) { - return FeatureStatus::Failed; - } - - mRenderMode = RENDER_DIRECT2D; - mUseDirectWrite = true; - return FeatureStatus::Available; -#else - return FeatureStatus::Unavailable; -#endif -} - -void +bool gfxWindowsPlatform::InitDWriteSupport() { -#ifdef CAIRO_HAS_DWRITE_FONT - // Enable when it's preffed on -and- we're using Vista or higher. Or when - // we're going to use D2D. - if (mDWriteFactory || (!mUseDirectWrite || !IsVistaOrLater())) { - return; - } + MOZ_ASSERT(!mDWriteFactory && IsVistaOrLater()); mozilla::ScopedGfxFeatureReporter reporter("DWrite"); decltype(DWriteCreateFactory)* createDWriteFactory = (decltype(DWriteCreateFactory)*) GetProcAddress(LoadLibraryW(L"dwrite.dll"), "DWriteCreateFactory"); - if (!createDWriteFactory) { - return; + return false; } // I need a direct pointer to be able to cast to IUnknown**, I also need to // remember to release this because the nsRefPtr will AddRef it. - IDWriteFactory *factory; + RefPtr factory; HRESULT hr = createDWriteFactory( DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), - reinterpret_cast(&factory)); - - if (SUCCEEDED(hr) && factory) { - mDWriteFactory = factory; - factory->Release(); - hr = mDWriteFactory->CreateTextAnalyzer(getter_AddRefs(mDWriteAnalyzer)); + (IUnknown **)((IDWriteFactory **)byRef(factory))); + if (FAILED(hr) || !factory) { + return false; } + mDWriteFactory = factory; + SetupClearTypeParams(); + reporter.SetSuccessful(); + return true; +} - if (hr == S_OK) { - reporter.SetSuccessful(); +bool +gfxWindowsPlatform::HandleDeviceReset() +{ + DeviceResetReason resetReason = DeviceResetReason::OK; + if (!DidRenderingDeviceReset(&resetReason)) { + return false; } -#endif + + Telemetry::Accumulate(Telemetry::DEVICE_RESET_REASON, uint32_t(resetReason)); + + // Remove devices and adapters. + mD3D10Device = nullptr; + mD3D11Device = nullptr; + mD3D11ContentDevice = nullptr; + mD3D11ImageBridgeDevice = nullptr; + mAdapter = nullptr; + Factory::SetDirect3D11Device(nullptr); + Factory::SetDirect3D10Device(nullptr); + + // Reset local state. Note: we leave feature status variables as-is. They + // will be recomputed by InitializeDevices(). + mIsWARP = false; + mHasDeviceReset = false; + mDoesD3D11TextureSharingWork = false; + mDeviceResetReason = DeviceResetReason::OK; + + imgLoader::Singleton()->ClearCache(true); + imgLoader::Singleton()->ClearCache(false); + gfxAlphaBoxBlur::ShutdownBlurCache(); + + InitializeDevices(); + return true; +} + +void +gfxWindowsPlatform::UpdateBackendPrefs() +{ + uint32_t canvasMask = BackendTypeBit(BackendType::CAIRO); + uint32_t contentMask = BackendTypeBit(BackendType::CAIRO); + BackendType defaultBackend = BackendType::CAIRO; + if (GetD2DStatus() == FeatureStatus::Available) { + mRenderMode = RENDER_DIRECT2D; + canvasMask |= BackendTypeBit(BackendType::DIRECT2D); + contentMask |= BackendTypeBit(BackendType::DIRECT2D); + if (GetD2D1Status() == FeatureStatus::Available) { + contentMask |= BackendTypeBit(BackendType::DIRECT2D1_1); + canvasMask |= BackendTypeBit(BackendType::DIRECT2D1_1); + defaultBackend = BackendType::DIRECT2D1_1; + } else { + defaultBackend = BackendType::DIRECT2D; + } + } else { + mRenderMode = RENDER_GDI; + canvasMask |= BackendTypeBit(BackendType::SKIA); + } + contentMask |= BackendTypeBit(BackendType::SKIA); + InitBackendPrefs(canvasMask, defaultBackend, contentMask, defaultBackend); } void gfxWindowsPlatform::UpdateRenderMode() { -/* Pick the default render mode for - * desktop. - */ - bool didReset = false; - DeviceResetReason resetReason = DeviceResetReason::OK; - if (DidRenderingDeviceReset(&resetReason)) { - Telemetry::Accumulate(Telemetry::DEVICE_RESET_REASON, uint32_t(resetReason)); - mD3D11DeviceInitialized = false; - mD3D11Device = nullptr; - mD3D11ContentDevice = nullptr; - mAdapter = nullptr; - mDeviceResetReason = DeviceResetReason::OK; - mHasDeviceReset = false; + bool didReset = HandleDeviceReset(); - imgLoader::Singleton()->ClearCache(true); - imgLoader::Singleton()->ClearCache(false); - gfxAlphaBoxBlur::ShutdownBlurCache(); - Factory::SetDirect3D11Device(nullptr); + UpdateBackendPrefs(); - didReset = true; - } - - mRenderMode = RENDER_GDI; - mUseDirectWrite = gfxPrefs::DirectWriteFontRenderingEnabled(); - - mD2DStatus = InitD2DSupport(); - InitDWriteSupport(); - - uint32_t canvasMask = BackendTypeBit(BackendType::CAIRO); - uint32_t contentMask = BackendTypeBit(BackendType::CAIRO); - BackendType defaultBackend = BackendType::CAIRO; - if (mRenderMode == RENDER_DIRECT2D) { - canvasMask |= BackendTypeBit(BackendType::DIRECT2D); - contentMask |= BackendTypeBit(BackendType::DIRECT2D); - if (gfxPrefs::Direct2DUse1_1() && Factory::SupportsD2D1() && - GetD3D11ContentDevice()) { - contentMask |= BackendTypeBit(BackendType::DIRECT2D1_1); - canvasMask |= BackendTypeBit(BackendType::DIRECT2D1_1); - defaultBackend = BackendType::DIRECT2D1_1; - } else { - defaultBackend = BackendType::DIRECT2D; - } - } else { - canvasMask |= BackendTypeBit(BackendType::SKIA); - } - contentMask |= BackendTypeBit(BackendType::SKIA); - InitBackendPrefs(canvasMask, defaultBackend, - contentMask, defaultBackend); - - if (didReset) { - mScreenReferenceDrawTarget = CreateOffscreenContentDrawTarget(IntSize(1, 1), SurfaceFormat::B8G8R8A8); - } + if (didReset) { + mScreenReferenceDrawTarget = + CreateOffscreenContentDrawTarget(IntSize(1, 1), SurfaceFormat::B8G8R8A8); + } } #ifdef CAIRO_HAS_D2D_SURFACE @@ -644,11 +595,6 @@ void gfxWindowsPlatform::VerifyD2DDevice(bool aAttemptForce) { #ifdef CAIRO_HAS_D2D_SURFACE - DriverInitCrashDetection detectCrashes; - if (detectCrashes.DisableAcceleration()) { - return; - } - if (mD3D10Device) { if (SUCCEEDED(mD3D10Device->GetDeviceRemovedReason())) { return; @@ -716,7 +662,6 @@ gfxWindowsPlatform::VerifyD2DDevice(bool aAttemptForce) gfxPlatformFontList* gfxWindowsPlatform::CreatePlatformFontList() { - mUsingGDIFonts = false; gfxPlatformFontList *pfl; #ifdef CAIRO_HAS_DWRITE_FONT // bug 630201 - older pre-RTM versions of Direct2D/DirectWrite cause odd @@ -730,11 +675,10 @@ gfxWindowsPlatform::CreatePlatformFontList() // but apparently it can - see bug 594865. // So we're going to fall back to GDI fonts & rendering. gfxPlatformFontList::Shutdown(); - SetRenderMode(RENDER_GDI); + DisableD2D(); } #endif pfl = new gfxGDIFontList(); - mUsingGDIFonts = true; if (NS_SUCCEEDED(pfl->InitFontList())) { return pfl; @@ -744,6 +688,22 @@ gfxWindowsPlatform::CreatePlatformFontList() return nullptr; } +// This function will permanently disable D2D for the session. It's intended to +// be used when, after initially chosing to use Direct2D, we encounter a +// scenario we can't support. +// +// This is called during gfxPlatform::Init() so at this point there should be no +// DrawTargetD2D/1 instances. +void +gfxWindowsPlatform::DisableD2D() +{ + mD2DStatus = FeatureStatus::Failed; + mD2D1Status = FeatureStatus::Failed; + Factory::SetDirect3D11Device(nullptr); + Factory::SetDirect3D10Device(nullptr); + UpdateBackendPrefs(); +} + already_AddRefed gfxWindowsPlatform::CreateOffscreenSurface(const IntSize& aSize, gfxImageFormat aFormat) @@ -1556,36 +1516,18 @@ gfxWindowsPlatform::GetD3D9DeviceManager() ID3D11Device* gfxWindowsPlatform::GetD3D11Device() { - if (mD3D11DeviceInitialized) { - return mD3D11Device; - } - - InitD3D11Devices(); - return mD3D11Device; } ID3D11Device* gfxWindowsPlatform::GetD3D11ContentDevice() { - if (mD3D11DeviceInitialized) { - return mD3D11ContentDevice; - } - - InitD3D11Devices(); - return mD3D11ContentDevice; } ID3D11Device* gfxWindowsPlatform::GetD3D11ImageBridgeDevice() { - if (mD3D11DeviceInitialized) { - return mD3D11ImageBridgeDevice; - } - - InitD3D11Devices(); - return mD3D11ImageBridgeDevice; } @@ -1918,31 +1860,42 @@ bool DoesD3D11AlphaTextureSharingWork(ID3D11Device *device) return DoesD3D11TextureSharingWorkInternal(device, DXGI_FORMAT_R8_UNORM, D3D11_BIND_SHADER_RESOURCE); } +static inline bool +CanUseWARP() +{ + if (gfxPrefs::LayersD3D11ForceWARP()) { + return true; + } + + // It seems like nvdxgiwrap makes a mess of WARP. See bug 1154703. + if (!IsWin8OrLater() || + gfxPrefs::LayersD3D11DisableWARP() || + GetModuleHandleA("nvdxgiwrap.dll")) + { + return false; + } + return true; +} + auto gfxWindowsPlatform::CheckD3D11Support() -> D3D11Status { if (gfxPrefs::LayersD3D11ForceWARP()) { - return D3D11Status::ForceWARP; + return D3D11Status::OnlyWARP; } - if (nsCOMPtr gfxInfo = services::GetGfxInfo()) { int32_t status; if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, &status))) { if (status != nsIGfxInfo::FEATURE_STATUS_OK) { - // See if we can use WARP instead. - // - // It seems like nvdxgiwrap makes a mess of WARP. See bug 1154703 for more. - if (gfxPrefs::LayersD3D11DisableWARP() || GetModuleHandleA("nvdxgiwrap.dll")) { + if (!CanUseWARP()) { return D3D11Status::Blocked; } - return D3D11Status::TryWARP; + return D3D11Status::OnlyWARP; } } } - - // Either nsIGfxInfo was bugged or we're not blacklisted. if (!GetDXGIAdapter()) { - return D3D11Status::TryWARP; + return D3D11Status::OnlyWARP; } return D3D11Status::Ok; } @@ -1953,7 +1906,7 @@ gfxWindowsPlatform::CheckD3D11Support() -> D3D11Status decltype(D3D11CreateDevice)* sD3D11CreateDeviceFn = nullptr; bool -gfxWindowsPlatform::AttemptD3D11DeviceCreation(const nsTArray& aFeatureLevels) +gfxWindowsPlatform::AttemptD3D11DeviceCreation() { RefPtr adapter = GetDXGIAdapter(); MOZ_ASSERT(adapter); @@ -1966,7 +1919,7 @@ gfxWindowsPlatform::AttemptD3D11DeviceCreation(const nsTArray // D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS // to prevent bug 1092260. IE 11 also uses this flag. D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, - aFeatureLevels.Elements(), aFeatureLevels.Length(), + mFeatureLevels.Elements(), mFeatureLevels.Length(), D3D11_SDK_VERSION, byRef(mD3D11Device), nullptr, nullptr); } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { gfxCriticalError() << "Crash during D3D11 device creation"; @@ -1986,15 +1939,14 @@ gfxWindowsPlatform::AttemptD3D11DeviceCreation(const nsTArray // Only test this when not using WARP since it can fail and cause // GetDeviceRemovedReason to return weird values. mDoesD3D11TextureSharingWork = ::DoesD3D11TextureSharingWork(mD3D11Device); + mD3D11Device->SetExceptionMode(0); mIsWARP = false; return true; } bool -gfxWindowsPlatform::AttemptWARPDeviceCreation(const nsTArray& aFeatureLevels) +gfxWindowsPlatform::AttemptWARPDeviceCreation() { - MOZ_ASSERT(!mD3D11Device); - ScopedGfxFeatureReporter reporterWARP("D3D11-WARP", gfxPrefs::LayersD3D11ForceWARP()); MOZ_SEH_TRY { @@ -2004,7 +1956,7 @@ gfxWindowsPlatform::AttemptWARPDeviceCreation(const nsTArray& // D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS // to prevent bug 1092260. IE 11 also uses this flag. D3D11_CREATE_DEVICE_BGRA_SUPPORT, - aFeatureLevels.Elements(), aFeatureLevels.Length(), + mFeatureLevels.Elements(), mFeatureLevels.Length(), D3D11_SDK_VERSION, byRef(mD3D11Device), nullptr, nullptr); if (FAILED(hr)) { @@ -2017,14 +1969,21 @@ gfxWindowsPlatform::AttemptWARPDeviceCreation(const nsTArray& } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { gfxCriticalError() << "Exception occurred initializing WARP D3D11 device!"; return false; + } + // Only test for texture sharing on Windows 8 since it puts the device into + // an unusable state if used on Windows 7 + if (IsWin8OrLater()) { + mDoesD3D11TextureSharingWork = ::DoesD3D11TextureSharingWork(mD3D11Device); + } + mD3D11Device->SetExceptionMode(0); mIsWARP = true; return true; } bool -gfxWindowsPlatform::AttemptD3D11ContentDeviceCreation(const nsTArray& aFeatureLevels) +gfxWindowsPlatform::AttemptD3D11ContentDeviceCreation() { HRESULT hr = E_INVALIDARG; MOZ_SEH_TRY { @@ -2033,24 +1992,35 @@ gfxWindowsPlatform::AttemptD3D11ContentDeviceCreation(const nsTArraySetExceptionMode(0); + + nsRefPtr multi; + mD3D11ContentDevice->QueryInterface(__uuidof(ID3D10Multithread), getter_AddRefs(multi)); + multi->SetMultithreadProtected(TRUE); + + Factory::SetDirect3D11Device(mD3D11ContentDevice); + return true; } bool -gfxWindowsPlatform::AttemptD3D11ImageBridgeDeviceCreation(const nsTArray& aFeatureLevels) +gfxWindowsPlatform::AttemptD3D11ImageBridgeDeviceCreation() { HRESULT hr = E_INVALIDARG; MOZ_SEH_TRY{ hr = sD3D11CreateDeviceFn(GetDXGIAdapter(), D3D_DRIVER_TYPE_UNKNOWN, nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT, - aFeatureLevels.Elements(), aFeatureLevels.Length(), + mFeatureLevels.Elements(), mFeatureLevels.Length(), D3D11_SDK_VERSION, byRef(mD3D11ImageBridgeDevice), nullptr, nullptr); } MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { return false; @@ -2061,112 +2031,196 @@ gfxWindowsPlatform::AttemptD3D11ImageBridgeDeviceCreation(const nsTArraySetExceptionMode(0); - - return DoesD3D11AlphaTextureSharingWork(mD3D11ImageBridgeDevice); + if (!DoesD3D11AlphaTextureSharingWork(mD3D11ImageBridgeDevice)) { + mD3D11ImageBridgeDevice = nullptr; + return false; + } + return true; } void -gfxWindowsPlatform::InitD3D11Devices() +gfxWindowsPlatform::InitializeDevices() { - // This function attempts to initialize our D3D11 devices. If the hardware - // is not blacklisted for D3D11 layers. This will first attempt to create a - // hardware accelerated device. If this creation fails or the hardware is - // blacklisted, then this function will abort if WARP is disabled, causing us - // to fallback to D3D9 or Basic layers. If WARP is not disabled it will use - // a WARP device which should always be available on Windows 7 and higher. - mD3D11DeviceInitialized = true; - mDoesD3D11TextureSharingWork = false; - - MOZ_ASSERT(!mD3D11Device); - + // If we previously crashed initializing devices, or if we're in safe mode, + // bail out now. DriverInitCrashDetection detectCrashes; - if (InSafeMode() || detectCrashes.DisableAcceleration()) { - mD3D11Status = FeatureStatus::Blocked; + if (detectCrashes.DisableAcceleration() || InSafeMode()) { + mAcceleration = FeatureStatus::Blocked; return; } - D3D11Status status = CheckD3D11Support(); - if (status == D3D11Status::Blocked) { + // If acceleration is disabled, we refuse to initialize anything. + if (!ShouldUseLayersAcceleration()) { + mAcceleration = FeatureStatus::Disabled; + return; + } + + // At this point, as far as we know, we can probably accelerate. + mAcceleration = FeatureStatus::Available; + + // If we're going to prefer D3D9, stop here. The rest of this function + // attempts to use D3D11 features. + if (gfxPrefs::LayersPreferD3D9()) { + mD3D11Status = FeatureStatus::Disabled; + return; + } + + // First, initialize D3D11. If this succeeds we attempt to use Direct2D. + InitializeD3D11(); + if (mD3D11Status == FeatureStatus::Available) { + InitializeD2D(); + } +} + +void +gfxWindowsPlatform::InitializeD3D11() +{ + // This function attempts to initialize our D3D11 devices, if the hardware + // is not blacklisted for D3D11 layers. This first attempt will try to create + // a hardware accelerated device. If this creation fails or the hardware is + // blacklisted, then this function will abort if WARP is disabled, causing us + // to fallback to D3D9 or Basic layers. If WARP is not disabled it will use + // a WARP device which should always be available on Windows 7 and higher. + + // Check if D3D11 is supported on this hardware. + D3D11Status support = CheckD3D11Support(); + if (support == D3D11Status::Blocked) { mD3D11Status = FeatureStatus::Blacklisted; return; } + // Check if D3D11 is available on this system. nsModuleHandle d3d11Module(LoadLibrarySystem32(L"d3d11.dll")); sD3D11CreateDeviceFn = (decltype(D3D11CreateDevice)*)GetProcAddress(d3d11Module, "D3D11CreateDevice"); - if (!sD3D11CreateDeviceFn) { // We should just be on Windows Vista or XP in this case. mD3D11Status = FeatureStatus::Unavailable; return; } - nsTArray featureLevels; - if (IsWin8OrLater()) { - featureLevels.AppendElement(D3D_FEATURE_LEVEL_11_1); + // If hardware acceleration is allowed, attempt to create a device. If this + // fails, we fall back to WARP. + if (support == D3D11Status::Ok && !AttemptD3D11DeviceCreation()) { + support = D3D11Status::OnlyWARP; } - featureLevels.AppendElement(D3D_FEATURE_LEVEL_11_0); - featureLevels.AppendElement(D3D_FEATURE_LEVEL_10_1); - featureLevels.AppendElement(D3D_FEATURE_LEVEL_10_0); - featureLevels.AppendElement(D3D_FEATURE_LEVEL_9_3); - - if (status == D3D11Status::Ok) { - if (!AttemptD3D11DeviceCreation(featureLevels)) { - status = D3D11Status::TryWARP; - } - } - - if (IsWin8OrLater() && - !gfxPrefs::LayersD3D11DisableWARP() && - (status == D3D11Status::TryWARP || status == D3D11Status::ForceWARP)) - { - AttemptWARPDeviceCreation(featureLevels); - mD3D11Status = FeatureStatus::Failed; - } - - // Only test for texture sharing on Windows 8 since it puts the device into - // an unusable state if used on Windows 7 - if (mD3D11Device && IsWin8OrLater()) { - mDoesD3D11TextureSharingWork = ::DoesD3D11TextureSharingWork(mD3D11Device); + if (support == D3D11Status::OnlyWARP && CanUseWARP()) { + AttemptWARPDeviceCreation(); } if (!mD3D11Device) { - // We could not get a D3D11 compositor, and there's nothing more we can try. + // Nothing more we can do. + mD3D11Status = FeatureStatus::Failed; return; } - mD3D11Device->SetExceptionMode(0); + // If we got here, we successfully got a D3D11 device. mD3D11Status = FeatureStatus::Available; - - // We create our device for D2D content drawing here. Normally we don't use - // D2D content drawing when using WARP. However when WARP is forced by - // default we will let Direct2D use WARP as well. - if (Factory::SupportsD2D1() && (!mIsWARP || (status == D3D11Status::ForceWARP))) { - if (!AttemptD3D11ContentDeviceCreation(featureLevels)) { - mD3D11ContentDevice = nullptr; - d3d11Module.disown(); - return; - } - - mD3D11ContentDevice->SetExceptionMode(0); - nsRefPtr multi; - mD3D11ContentDevice->QueryInterface(__uuidof(ID3D10Multithread), getter_AddRefs(multi)); - multi->SetMultithreadProtected(TRUE); - - Factory::SetDirect3D11Device(mD3D11ContentDevice); - } + MOZ_ASSERT(mD3D11Device); if (!mIsWARP) { - if (!AttemptD3D11ImageBridgeDeviceCreation(featureLevels)) { - mD3D11ImageBridgeDevice = nullptr; - } + AttemptD3D11ImageBridgeDeviceCreation(); } // We leak these everywhere and we need them our entire runtime anyway, let's - // leak it here as well. + // leak it here as well. We keep the pointer to sD3D11CreateDeviceFn around + // as well for D2D1 and device resets. d3d11Module.disown(); } +static bool +IsD2DBlacklisted() +{ + nsCOMPtr gfxInfo = services::GetGfxInfo(); + if (gfxInfo) { + int32_t status; + if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT2D, &status))) { + if (status != nsIGfxInfo::FEATURE_STATUS_OK) { + return true; + } + } + } + return false; +} + +void +gfxWindowsPlatform::InitializeD2D() +{ + if (!gfxPrefs::Direct2DForceEnabled()) { + if (IsD2DBlacklisted()) { + mD2DStatus = FeatureStatus::Blacklisted; + return; + } + } + + // Do not ever try to use D2D if it's explicitly disabled. + if (gfxPrefs::Direct2DDisabled()) { + mD2DStatus = FeatureStatus::Disabled; + return; + } + + // Direct2D is only Vista or higher, but we require a D3D11 compositor to + // use it. (This check may be implied by the fact that we do not get here + // without a D3D11 compositor device.) + if (!IsVistaOrLater()) { + mD2DStatus = FeatureStatus::Unavailable; + return; + } + if (!mDoesD3D11TextureSharingWork) { + mD2DStatus = FeatureStatus::Failed; + return; + } + + // Using Direct2D depends on DWrite support. + if (!mDWriteFactory && !InitDWriteSupport()) { + mD2DStatus = FeatureStatus::Failed; + return; + } + + // Initialize D2D 1.1. + InitializeD2D1(); + + // Initialize D2D 1.0. + VerifyD2DDevice(gfxPrefs::Direct2DForceEnabled()); + if (!mD3D10Device) { + mD2DStatus = FeatureStatus::Failed; + return; + } + + mD2DStatus = FeatureStatus::Available; +} + +bool +gfxWindowsPlatform::InitializeD2D1() +{ + ScopedGfxFeatureReporter d2d1_1("D2D1.1"); + + if (!Factory::SupportsD2D1()) { + mD2D1Status = FeatureStatus::Unavailable; + return false; + } + if (!gfxPrefs::Direct2DUse1_1()) { + mD2D1Status = FeatureStatus::Disabled; + return false; + } + + // Normally we don't use D2D content drawing when using WARP. However if + // WARP is force-enabled, we wlil let Direct2D use WARP as well. + if (mIsWARP && !gfxPrefs::LayersD3D11ForceWARP()) { + mD2D1Status = FeatureStatus::Blocked; + return false; + } + + if (!AttemptD3D11ContentDeviceCreation()) { + mD2D1Status = FeatureStatus::Failed; + return false; + } + + mD2D1Status = FeatureStatus::Available; + d2d1_1.SetSuccessful(); + return true; +} + already_AddRefed gfxWindowsPlatform::CreateD3D11DecoderDevice() { @@ -2442,23 +2496,33 @@ gfxWindowsPlatform::GetAcceleratedCompositorBackends(nsTArray& aB } } +// Some features are dependent on other features. If this is the case, we +// try to propagate the status of the parent feature if it wasn't available. FeatureStatus -gfxWindowsPlatform::GetD2D1Status() +gfxWindowsPlatform::GetD3D11Status() const { - if (GetD2DStatus() != FeatureStatus::Available || - !Factory::SupportsD2D1()) - { + if (mAcceleration != FeatureStatus::Available) { + return mAcceleration; + } + return mD3D11Status; +} + +FeatureStatus +gfxWindowsPlatform::GetD2DStatus() const +{ + if (GetD3D11Status() != FeatureStatus::Available) { return FeatureStatus::Unavailable; } + return mD2DStatus; +} - if (!GetD3D11ContentDevice()) { - return FeatureStatus::Failed; +FeatureStatus +gfxWindowsPlatform::GetD2D1Status() const +{ + if (GetD3D11Status() != FeatureStatus::Available) { + return FeatureStatus::Unavailable; } - - if (!gfxPrefs::Direct2DUse1_1()) { - return FeatureStatus::Disabled; - } - return FeatureStatus::Available; + return mD2D1Status; } unsigned diff --git a/gfx/thebes/gfxWindowsPlatform.h b/gfx/thebes/gfxWindowsPlatform.h index 3cfcc85dceb..127e7189f39 100644 --- a/gfx/thebes/gfxWindowsPlatform.h +++ b/gfx/thebes/gfxWindowsPlatform.h @@ -229,9 +229,8 @@ public: #ifdef CAIRO_HAS_DWRITE_FONT IDWriteFactory *GetDWriteFactory() { return mDWriteFactory; } - inline bool DWriteEnabled() { return mUseDirectWrite; } + inline bool DWriteEnabled() { return !!mDWriteFactory; } inline DWRITE_MEASURING_MODE DWriteMeasuringMode() { return mMeasuringMode; } - IDWriteTextAnalyzer *GetDWriteAnalyzer() { return mDWriteAnalyzer; } IDWriteRenderingParams *GetRenderingParams(TextRenderingMode aRenderMode) { return mRenderingParams[aRenderMode]; } @@ -263,17 +262,18 @@ public: } bool SupportsApzTouchInput() const override; + // Recreate devices as needed for a device reset. Returns true if a device + // reset occurred. + bool HandleDeviceReset(); + void UpdateBackendPrefs(); + // Return the diagnostic status of DirectX initialization. If // initialization has not been attempted, this returns // FeatureStatus::Unused. - mozilla::gfx::FeatureStatus GetD3D11Status() const { - return mD3D11Status; - } - mozilla::gfx::FeatureStatus GetD2DStatus() const { - return mD2DStatus; - } + mozilla::gfx::FeatureStatus GetD3D11Status() const; + mozilla::gfx::FeatureStatus GetD2DStatus() const; + mozilla::gfx::FeatureStatus GetD2D1Status() const; unsigned GetD3D11Version(); - mozilla::gfx::FeatureStatus GetD2D1Status(); virtual already_AddRefed CreateHardwareVsyncSource() override; static mozilla::Atomic sD3D11MemoryUsed; @@ -286,6 +286,7 @@ protected: return true; } void GetAcceleratedCompositorBackends(nsTArray& aBackends); + virtual void GetPlatformCMSOutputProfile(void* &mem, size_t &size); protected: RenderMode mRenderMode; @@ -296,34 +297,31 @@ protected: private: void Init(); - void InitD3D11Devices(); + void InitializeDevices(); + void InitializeD3D11(); + void InitializeD2D(); + bool InitializeD2D1(); + bool InitDWriteSupport(); - // Used by InitD3D11Devices(). + void DisableD2D(); + + // Used by InitializeD3D11(). enum class D3D11Status { Ok, - TryWARP, - ForceWARP, + OnlyWARP, Blocked }; D3D11Status CheckD3D11Support(); - bool AttemptD3D11DeviceCreation(const nsTArray& aFeatureLevels); - bool AttemptWARPDeviceCreation(const nsTArray& aFeatureLevels); - bool AttemptD3D11ImageBridgeDeviceCreation(const nsTArray& aFeatureLevels); - bool AttemptD3D11ContentDeviceCreation(const nsTArray& aFeatureLevels); - - // Used by UpdateRenderMode(). - mozilla::gfx::FeatureStatus InitD2DSupport(); - void InitDWriteSupport(); + bool AttemptD3D11DeviceCreation(); + bool AttemptWARPDeviceCreation(); + bool AttemptD3D11ImageBridgeDeviceCreation(); + bool AttemptD3D11ContentDeviceCreation(); IDXGIAdapter1 *GetDXGIAdapter(); bool IsDeviceReset(HRESULT hr, DeviceResetReason* aReason); - bool mUseDirectWrite; - bool mUsingGDIFonts; - #ifdef CAIRO_HAS_DWRITE_FONT nsRefPtr mDWriteFactory; - nsRefPtr mDWriteAnalyzer; nsRefPtr mRenderingParams[TEXT_RENDERING_COUNT]; DWRITE_MEASURING_MODE mMeasuringMode; #endif @@ -333,17 +331,20 @@ private: mozilla::RefPtr mD3D11Device; mozilla::RefPtr mD3D11ContentDevice; mozilla::RefPtr mD3D11ImageBridgeDevice; - bool mD3D11DeviceInitialized; mozilla::RefPtr mD3D11ReadbackManager; bool mIsWARP; bool mHasDeviceReset; bool mDoesD3D11TextureSharingWork; DeviceResetReason mDeviceResetReason; + // These should not be accessed directly. Use the Get[Feature]Status + // accessors instead. + mozilla::gfx::FeatureStatus mAcceleration; mozilla::gfx::FeatureStatus mD3D11Status; mozilla::gfx::FeatureStatus mD2DStatus; + mozilla::gfx::FeatureStatus mD2D1Status; - virtual void GetPlatformCMSOutputProfile(void* &mem, size_t &size); + nsTArray mFeatureLevels; }; #endif /* GFX_WINDOWS_PLATFORM_H */