From dc0138c34ad21c11b39010a6d573af68119fa45b Mon Sep 17 00:00:00 2001 From: David Anderson Date: Sun, 19 Jul 2015 14:50:35 -0700 Subject: [PATCH] Add compositor, layers, and rendering info to nsIGfxInfo. (bug 1179051 part 5, r=mattwoodrow) --- gfx/src/gfxTelemetry.cpp | 36 +++++++++++++ gfx/src/gfxTelemetry.h | 46 ++++++++++++++++ gfx/src/moz.build | 2 + gfx/thebes/gfxPlatform.cpp | 25 +++++++++ gfx/thebes/gfxPlatform.h | 15 ++++++ gfx/thebes/gfxWindowsPlatform.cpp | 85 +++++++++++++++++++++++------- gfx/thebes/gfxWindowsPlatform.h | 18 ++++++- widget/GfxInfoBase.cpp | 87 +++++++++++++++++++++++++++++++ widget/GfxInfoBase.h | 10 ++++ widget/nsBaseWidget.cpp | 2 + widget/nsIGfxInfo.idl | 28 +++++++++- widget/windows/GfxInfo.cpp | 36 +++++++++++++ widget/windows/GfxInfo.h | 2 + 13 files changed, 371 insertions(+), 21 deletions(-) create mode 100644 gfx/src/gfxTelemetry.cpp create mode 100644 gfx/src/gfxTelemetry.h diff --git a/gfx/src/gfxTelemetry.cpp b/gfx/src/gfxTelemetry.cpp new file mode 100644 index 00000000000..027c6761ed7 --- /dev/null +++ b/gfx/src/gfxTelemetry.cpp @@ -0,0 +1,36 @@ +/* -*- Mode: IDL; tab-width: 2; 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 "gfxTelemetry.h" + +namespace mozilla { +namespace gfx { + +const char* +FeatureStatusToString(FeatureStatus aStatus) +{ + switch (aStatus) { + case FeatureStatus::Unused: + return "unused"; + case FeatureStatus::Unavailable: + return "unavailable"; + case FeatureStatus::Blocked: + return "blocked"; + case FeatureStatus::Blacklisted: + return "blacklisted"; + case FeatureStatus::Failed: + return "failed"; + case FeatureStatus::Disabled: + return "disabled"; + case FeatureStatus::Available: + return "available"; + default: + MOZ_ASSERT_UNREACHABLE("missing status case"); + return "unknown"; + } +} + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/src/gfxTelemetry.h b/gfx/src/gfxTelemetry.h new file mode 100644 index 00000000000..e65c107c389 --- /dev/null +++ b/gfx/src/gfxTelemetry.h @@ -0,0 +1,46 @@ +/* -*- Mode: IDL; tab-width: 2; 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/. */ +#ifndef gfx_src_gfxTelemetry_h__ +#define gfx_src_gfxTelemetry_h__ + +namespace mozilla { +namespace gfx { + +// Describes the status of a graphics feature, in terms of whether or not we've +// attempted to initialize the feature, and if so, whether or not it succeeded +// (and if not, why). +enum class FeatureStatus +{ + // This feature has not been requested. + Unused, + + // This feature is unavailable due to Safe Mode or not being included with + // the operating system. + Unavailable, + + // This feature was blocked for reasons outside the blacklist, such as a + // runtime test failing. + Blocked, + + // This feature has been blocked by the graphics blacklist. + Blacklisted, + + // This feature was attempted but failed to activate. + Failed, + + // This feature was explicitly disabled by the user. + Disabled, + + // This feature is available for use. + Available +}; + +const char* FeatureStatusToString(FeatureStatus aStatus); + +} // namespace gfx +} // namespace mozilla + +#endif // gfx_src_gfxTelemetry_h__ diff --git a/gfx/src/moz.build b/gfx/src/moz.build index 8f9bff14151..3d26b88016c 100644 --- a/gfx/src/moz.build +++ b/gfx/src/moz.build @@ -18,6 +18,7 @@ EXPORTS += [ 'FilterSupport.h', 'gfxCore.h', 'gfxCrashReporterUtils.h', + 'gfxTelemetry.h', 'nsBoundingMetrics.h', 'nsColor.h', 'nsColorNameList.h', @@ -56,6 +57,7 @@ UNIFIED_SOURCES += [ 'DriverInitCrashDetection.cpp', 'FilterSupport.cpp', 'gfxCrashReporterUtils.cpp', + 'gfxTelemetry.cpp', 'nsColor.cpp', 'nsFont.cpp', 'nsFontMetrics.cpp', diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index c9bb910a078..041e90d77cf 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -379,6 +379,7 @@ gfxPlatform::gfxPlatform() , mTileHeight(-1) , mAzureCanvasBackendCollector(this, &gfxPlatform::GetAzureBackendInfo) , mApzSupportCollector(this, &gfxPlatform::GetApzSupportInfo) + , mCompositorBackend(layers::LayersBackend::LAYERS_NONE) { mAllowDownloadableFonts = UNINITIALIZED_VALUE; mFallbackUsesCmaps = UNINITIALIZED_VALUE; @@ -410,6 +411,12 @@ gfxPlatform::GetPlatform() return gPlatform; } +bool +gfxPlatform::Initialized() +{ + return !!gPlatform; +} + void RecordingPrefChanged(const char *aPrefName, void *aClosure) { if (Preferences::GetBool("gfx.2d.recording", false)) { @@ -2444,3 +2451,21 @@ gfxPlatform::GetCompositorBackends(bool useAcceleration, nsTArray obsvc = services::GetObserverService()) { + obsvc->NotifyObservers(nullptr, "compositor:created", nullptr); + } +} diff --git a/gfx/thebes/gfxPlatform.h b/gfx/thebes/gfxPlatform.h index 1958665aa97..23a57cebdd8 100644 --- a/gfx/thebes/gfxPlatform.h +++ b/gfx/thebes/gfxPlatform.h @@ -159,6 +159,12 @@ public: */ static gfxPlatform *GetPlatform(); + /** + * Returns whether or not graphics has been initialized yet. This is + * intended for Telemetry where we don't necessarily want to initialize + * graphics just to observe its state. + */ + static bool Initialized(); /** * Shut down Thebes. @@ -631,6 +637,11 @@ public: */ static bool PerfWarnings(); + void NotifyCompositorCreated(mozilla::layers::LayersBackend aBackend); + mozilla::layers::LayersBackend GetCompositorBackend() const { + return mCompositorBackend; + } + protected: gfxPlatform(); virtual ~gfxPlatform(); @@ -751,6 +762,10 @@ private: mozilla::RefPtr mRecorder; mozilla::RefPtr mSkiaGlue; + + // Backend that we are compositing with. NONE, if no compositor has been + // created yet. + mozilla::layers::LayersBackend mCompositorBackend; }; #endif /* GFX_PLATFORM_H */ diff --git a/gfx/thebes/gfxWindowsPlatform.cpp b/gfx/thebes/gfxWindowsPlatform.cpp index 0fd2090c125..0b7d1f59cfe 100755 --- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -377,6 +377,8 @@ gfxWindowsPlatform::gfxWindowsPlatform() , mIsWARP(false) , mHasDeviceReset(false) , mDoesD3D11TextureSharingWork(false) + , mD3D11Status(FeatureStatus::Unused) + , mD2DStatus(FeatureStatus::Unused) { mUseClearTypeForDownloadableFonts = UNINITIALIZED_VALUE; mUseClearTypeAlways = UNINITIALIZED_VALUE; @@ -436,7 +438,7 @@ gfxWindowsPlatform::CanUseHardwareVideoDecoding() return !IsWARP() && gfxPlatform::CanUseHardwareVideoDecoding(); } -void +FeatureStatus gfxWindowsPlatform::InitD2DSupport() { #ifdef CAIRO_HAS_D2D_SURFACE @@ -458,30 +460,41 @@ gfxWindowsPlatform::InitD2DSupport() // If D2D is blocked or D3D9 is prefered, and D2D is not force-enabled, then // we don't attempt to use D2D. - if ((d2dBlocked || gfxPrefs::LayersPreferD3D9()) && !gfxPrefs::Direct2DForceEnabled()) { - return; + 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; + return FeatureStatus::Disabled; } - ID3D11Device* device = GetD3D11Device(); - if (IsVistaOrLater() && - !InSafeMode() && - device && - mDoesD3D11TextureSharingWork) - { - VerifyD2DDevice(gfxPrefs::Direct2DForceEnabled()); - if (mD3D10Device && GetD3D11Device()) { - mRenderMode = RENDER_DIRECT2D; - mUseDirectWrite = true; - } - } else { - mD3D10Device = nullptr; + 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 } @@ -552,7 +565,7 @@ gfxWindowsPlatform::UpdateRenderMode() mRenderMode = RENDER_GDI; mUseDirectWrite = gfxPrefs::DirectWriteFontRenderingEnabled(); - InitD2DSupport(); + mD2DStatus = InitD2DSupport(); InitDWriteSupport(); uint32_t canvasMask = BackendTypeBit(BackendType::CAIRO); @@ -2056,15 +2069,17 @@ gfxWindowsPlatform::InitD3D11Devices() mD3D11DeviceInitialized = true; mDoesD3D11TextureSharingWork = false; - MOZ_ASSERT(!mD3D11Device); + MOZ_ASSERT(!mD3D11Device); DriverInitCrashDetection detectCrashes; if (InSafeMode() || detectCrashes.DisableAcceleration()) { + mD3D11Status = FeatureStatus::Blocked; return; } D3D11Status status = CheckD3D11Support(); if (status == D3D11Status::Blocked) { + mD3D11Status = FeatureStatus::Blacklisted; return; } @@ -2074,6 +2089,7 @@ gfxWindowsPlatform::InitD3D11Devices() if (!sD3D11CreateDeviceFn) { // We should just be on Windows Vista or XP in this case. + mD3D11Status = FeatureStatus::Unavailable; return; } @@ -2097,6 +2113,7 @@ gfxWindowsPlatform::InitD3D11Devices() (status == D3D11Status::TryWARP || status == D3D11Status::ForceWARP)) { AttemptWARPDeviceCreation(featureLevels); + mD3D11Status = FeatureStatus::Failed; } if (!mD3D11Device) { @@ -2105,6 +2122,7 @@ gfxWindowsPlatform::InitD3D11Devices() } mD3D11Device->SetExceptionMode(0); + 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 @@ -2409,3 +2427,32 @@ gfxWindowsPlatform::GetAcceleratedCompositorBackends(nsTArray& aB } } } + +FeatureStatus +gfxWindowsPlatform::GetD2D1Status() +{ + if (GetD2DStatus() != FeatureStatus::Available || + !Factory::SupportsD2D1()) + { + return FeatureStatus::Unavailable; + } + + if (!GetD3D11ContentDevice()) { + return FeatureStatus::Failed; + } + + if (!gfxPrefs::Direct2DUse1_1()) { + return FeatureStatus::Disabled; + } + return FeatureStatus::Available; +} + +unsigned +gfxWindowsPlatform::GetD3D11Version() +{ + ID3D11Device* device = GetD3D11Device(); + if (!device) { + return 0; + } + return device->GetFeatureLevel(); +} diff --git a/gfx/thebes/gfxWindowsPlatform.h b/gfx/thebes/gfxWindowsPlatform.h index 24438eb4f10..eb730b3a8cf 100644 --- a/gfx/thebes/gfxWindowsPlatform.h +++ b/gfx/thebes/gfxWindowsPlatform.h @@ -20,6 +20,7 @@ #include "gfxDWriteFonts.h" #endif #include "gfxPlatform.h" +#include "gfxTelemetry.h" #include "gfxTypes.h" #include "mozilla/Attributes.h" #include "mozilla/Atomics.h" @@ -261,6 +262,18 @@ public: } bool SupportsApzTouchInput() const override; + // 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; + } + unsigned GetD3D11Version(); + mozilla::gfx::FeatureStatus GetD2D1Status(); + virtual already_AddRefed CreateHardwareVsyncSource() override; static mozilla::Atomic sD3D11MemoryUsed; static mozilla::Atomic sD3D9MemoryUsed; @@ -298,7 +311,7 @@ private: bool AttemptD3D11ContentDeviceCreation(const nsTArray& aFeatureLevels); // Used by UpdateRenderMode(). - void InitD2DSupport(); + mozilla::gfx::FeatureStatus InitD2DSupport(); void InitDWriteSupport(); IDXGIAdapter1 *GetDXGIAdapter(); @@ -326,6 +339,9 @@ private: bool mDoesD3D11TextureSharingWork; DeviceResetReason mDeviceResetReason; + mozilla::gfx::FeatureStatus mD3D11Status; + mozilla::gfx::FeatureStatus mD2DStatus; + virtual void GetPlatformCMSOutputProfile(void* &mem, size_t &size); }; diff --git a/widget/GfxInfoBase.cpp b/widget/GfxInfoBase.cpp index 61cb5c98f92..b9d3022444e 100644 --- a/widget/GfxInfoBase.cpp +++ b/widget/GfxInfoBase.cpp @@ -33,6 +33,7 @@ #include "mozilla/gfx/2D.h" #include "mozilla/gfx/Logging.h" #include "gfxPrefs.h" +#include "gfxPlatform.h" #if defined(MOZ_CRASHREPORTER) #include "nsExceptionHandler.h" @@ -1179,6 +1180,92 @@ GfxInfoBase::GetMonitors(JSContext* aCx, JS::MutableHandleValue aResult) return NS_OK; } +static const char* +GetLayersBackendName(layers::LayersBackend aBackend) +{ + switch (aBackend) { + case layers::LayersBackend::LAYERS_NONE: + return "none"; + case layers::LayersBackend::LAYERS_OPENGL: + return "opengl"; + case layers::LayersBackend::LAYERS_D3D9: + return "d3d9"; + case layers::LayersBackend::LAYERS_D3D11: + return "d3d11"; + case layers::LayersBackend::LAYERS_CLIENT: + return "client"; + case layers::LayersBackend::LAYERS_BASIC: + return "basic"; + default: + MOZ_ASSERT_UNREACHABLE("unknown layers backend"); + return "unknown"; + } +} + +nsresult +GfxInfoBase::GetFeatures(JSContext* aCx, JS::MutableHandle aOut) +{ + JS::Rooted obj(aCx, JS_NewPlainObject(aCx)); + if (!obj) { + return NS_ERROR_OUT_OF_MEMORY; + } + aOut.setObject(*obj); + + layers::LayersBackend backend = gfxPlatform::Initialized() + ? gfxPlatform::GetPlatform()->GetCompositorBackend() + : layers::LayersBackend::LAYERS_NONE; + const char* backendName = GetLayersBackendName(backend); + { + JS::Rooted str(aCx, JS_NewStringCopyZ(aCx, backendName)); + JS::Rooted val(aCx, StringValue(str)); + JS_SetProperty(aCx, obj, "compositor", val); + } + + // If graphics isn't initialized yet, just stop now. + if (!gfxPlatform::Initialized()) { + return NS_OK; + } + + DescribeFeatures(aCx, obj); + return NS_OK; +} + +void +GfxInfoBase::DescribeFeatures(JSContext* cx, JS::Handle aOut) +{ +} + +bool +GfxInfoBase::InitFeatureObject(JSContext* aCx, + JS::Handle aContainer, + const char* aName, + mozilla::gfx::FeatureStatus aFeatureStatus, + JS::MutableHandle aOutObj) +{ + JS::Rooted obj(aCx, JS_NewPlainObject(aCx)); + if (!obj) { + return false; + } + + const char* status = FeatureStatusToString(aFeatureStatus); + + // Set "status". + { + JS::Rooted str(aCx, JS_NewStringCopyZ(aCx, status)); + JS::Rooted val(aCx, JS::StringValue(str)); + JS_SetProperty(aCx, obj, "status", val); + } + + // Add the feature object to the container. + { + JS::Rooted val(aCx, JS::ObjectValue(*obj)); + JS_SetProperty(aCx, aContainer, aName, val); + } + + aOutObj.set(obj); + return true; +} + GfxInfoCollectorBase::GfxInfoCollectorBase() { GfxInfoBase::AddCollector(this); diff --git a/widget/GfxInfoBase.h b/widget/GfxInfoBase.h index 4188912ba39..dff9cf0a589 100644 --- a/widget/GfxInfoBase.h +++ b/widget/GfxInfoBase.h @@ -19,6 +19,7 @@ #include "nsTArray.h" #include "nsString.h" #include "GfxInfoCollector.h" +#include "gfxTelemetry.h" #include "nsIGfxInfoDebug.h" #include "mozilla/Mutex.h" #include "js/Value.h" @@ -58,6 +59,7 @@ public: NS_IMETHOD GetFailures(uint32_t *failureCount, int32_t** indices, char ***failures) override; NS_IMETHOD_(void) LogFailure(const nsACString &failure) override; NS_IMETHOD GetInfo(JSContext*, JS::MutableHandle) override; + NS_IMETHOD GetFeatures(JSContext*, JS::MutableHandle) override; // Initialization function. If you override this, you must call this class's // version of Init first. @@ -103,6 +105,14 @@ protected: // (while subclasses check for more specific ones). virtual const nsTArray& GetGfxDriverInfo() = 0; + virtual void DescribeFeatures(JSContext* aCx, JS::Handle obj); + bool InitFeatureObject( + JSContext* aCx, + JS::Handle aContainer, + const char* aName, + mozilla::gfx::FeatureStatus aFeatureStatus, + JS::MutableHandle aOutObj); + private: virtual int32_t FindBlocklistedDeviceInList(const nsTArray& aDriverInfo, nsAString& aSuggestedVersion, diff --git a/widget/nsBaseWidget.cpp b/widget/nsBaseWidget.cpp index 087c6a181ff..fe4f693bc25 100644 --- a/widget/nsBaseWidget.cpp +++ b/widget/nsBaseWidget.cpp @@ -1131,6 +1131,8 @@ void nsBaseWidget::CreateCompositor(int aWidth, int aHeight) WindowUsesOMTC(); mLayerManager = lm.forget(); + + gfxPlatform::GetPlatform()->NotifyCompositorCreated(mLayerManager->GetCompositorBackendType()); } bool nsBaseWidget::ShouldUseOffMainThreadCompositing() diff --git a/widget/nsIGfxInfo.idl b/widget/nsIGfxInfo.idl index 8b10556b2fa..1e76c22c897 100644 --- a/widget/nsIGfxInfo.idl +++ b/widget/nsIGfxInfo.idl @@ -8,7 +8,7 @@ /* NOTE: this interface is completely undesigned, not stable and likely to change */ -[scriptable, uuid(47eedfa0-f7cb-445b-b5cf-a2ca83600560)] +[scriptable, uuid(98690931-c9a5-4675-9ab4-90932ec32bf2)] interface nsIGfxInfo : nsISupports { /* @@ -147,5 +147,31 @@ interface nsIGfxInfo : nsISupports [implicit_jscontext] jsval getInfo(); + + // Returns an object containing information about graphics features. It is + // intended to be directly included into the Telemetry environment. + // + // "layers": + // { + // "compositor": "d3d9", "d3d11", "opengl", "basic", or "none" + // // ("none" indicates no compositors have been created) + // // Feature is one of "d3d9", "d3d11", "opengl", "basic", or "d2d". + // "": { + // // Each backend can have one of the following statuses: + // // "unused" - This feature has not been requested. + // // "unavailable" - OS version or restriction prevents use. + // // "blocked" - An internal condition (such as safe mode) prevents use. + // // "blacklisted" - Blocked due to a blacklist restriction. + // // "disabled" - User explicitly disabled this default feature. + // // "failed" - Feature failed to initialize. + // // "available" - User has this feature available by default. + // "status": "", + // "version": "", + // "warp": true|false, // D3D11 only. + // "textureSharing": true|false, // D3D11 only. + // } + // } + [implicit_jscontext] + jsval getFeatures(); }; diff --git a/widget/windows/GfxInfo.cpp b/widget/windows/GfxInfo.cpp index eb1f4ec81df..9400ecbda47 100644 --- a/widget/windows/GfxInfo.cpp +++ b/widget/windows/GfxInfo.cpp @@ -1255,6 +1255,42 @@ GfxInfo::FindMonitors(JSContext* aCx, JS::HandleObject aOutArray) return NS_OK; } +void +GfxInfo::DescribeFeatures(JSContext* aCx, JS::Handle aObj) +{ + JS::Rooted obj(aCx); + + gfxWindowsPlatform* platform = gfxWindowsPlatform::GetPlatform(); + + gfx::FeatureStatus d3d11 = platform->GetD3D11Status(); + if (!InitFeatureObject(aCx, aObj, "d3d11", d3d11, &obj)) { + return; + } + if (d3d11 == gfx::FeatureStatus::Available) { + JS::Rooted val(aCx, JS::Int32Value(platform->GetD3D11Version())); + JS_SetProperty(aCx, obj, "version", val); + + val = JS::BooleanValue(platform->IsWARP()); + JS_SetProperty(aCx, obj, "warp", val); + + val = JS::BooleanValue(platform->DoesD3D11TextureSharingWork()); + JS_SetProperty(aCx, obj, "textureSharing", val); + } + + gfx::FeatureStatus d2d = platform->GetD2DStatus(); + if (!InitFeatureObject(aCx, aObj, "d2d", d2d, &obj)) { + return; + } + { + const char* version = "1.0"; + if (platform->GetD2D1Status() == gfx::FeatureStatus::Available) + version = "1.1"; + JS::Rooted str(aCx, JS_NewStringCopyZ(aCx, version)); + JS::Rooted val(aCx, JS::StringValue(str)); + JS_SetProperty(aCx, obj, "version", val); + } +} + #ifdef DEBUG // Implement nsIGfxInfoDebug diff --git a/widget/windows/GfxInfo.h b/widget/windows/GfxInfo.h index 5fd361900ba..5d05b149ebe 100644 --- a/widget/windows/GfxInfo.h +++ b/widget/windows/GfxInfo.h @@ -68,6 +68,8 @@ protected: OperatingSystem* aOS = nullptr); virtual const nsTArray& GetGfxDriverInfo(); + void DescribeFeatures(JSContext* cx, JS::Handle aOut) override; + private: void AddCrashReportAnnotations();