Add compositor, layers, and rendering info to nsIGfxInfo. (bug 1179051 part 5, r=mattwoodrow)
authorDavid Anderson <danderson@mozilla.com>
Sun, 19 Jul 2015 14:50:35 -0700
changeset 253625 5b4aec6951dd81465c423029768a1d4880e20729
parent 253624 06340c449def11b56d50573d9fc7898de553b8f5
child 253626 7e15449cb6e824d959629de6694744be566554a4
push id62511
push userdanderson@mozilla.com
push dateMon, 20 Jul 2015 08:41:27 +0000
treeherdermozilla-inbound@7e15449cb6e8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs1179051
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Add compositor, layers, and rendering info to nsIGfxInfo. (bug 1179051 part 5, r=mattwoodrow)
gfx/src/gfxTelemetry.cpp
gfx/src/gfxTelemetry.h
gfx/src/moz.build
gfx/thebes/gfxPlatform.cpp
gfx/thebes/gfxPlatform.h
gfx/thebes/gfxWindowsPlatform.cpp
gfx/thebes/gfxWindowsPlatform.h
widget/GfxInfoBase.cpp
widget/GfxInfoBase.h
widget/nsBaseWidget.cpp
widget/nsIGfxInfo.idl
widget/windows/GfxInfo.cpp
widget/windows/GfxInfo.h
new file mode 100644
--- /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
new file mode 100644
--- /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__
--- a/gfx/src/moz.build
+++ b/gfx/src/moz.build
@@ -13,16 +13,17 @@ XPIDL_MODULE = 'gfx'
 
 DEFINES['MOZ_APP_VERSION'] = '"%s"' % CONFIG['MOZ_APP_VERSION']
 
 EXPORTS += [
     'DriverInitCrashDetection.h',
     'FilterSupport.h',
     'gfxCore.h',
     'gfxCrashReporterUtils.h',
+    'gfxTelemetry.h',
     'nsBoundingMetrics.h',
     'nsColor.h',
     'nsColorNameList.h',
     'nsColorNames.h',
     'nsCoord.h',
     'nsDeviceContext.h',
     'nsFont.h',
     'nsFontMetrics.h',
@@ -51,16 +52,17 @@ if CONFIG['MOZ_X11']:
     SOURCES += [
         'X11Util.cpp',
     ]
 
 UNIFIED_SOURCES += [
     'DriverInitCrashDetection.cpp',
     'FilterSupport.cpp',
     'gfxCrashReporterUtils.cpp',
+    'gfxTelemetry.cpp',
     'nsColor.cpp',
     'nsFont.cpp',
     'nsFontMetrics.cpp',
     'nsRect.cpp',
     'nsRegion.cpp',
     'nsRenderingContext.cpp',
     'nsScriptableRegion.cpp',
     'nsThebesFontEnumerator.cpp',
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -374,16 +374,17 @@ static nsIAtom* PrefLangToLangGroups(uin
          : nsGkAtoms::Unicode;
 }
 
 gfxPlatform::gfxPlatform()
   : mTileWidth(-1)
   , mTileHeight(-1)
   , mAzureCanvasBackendCollector(this, &gfxPlatform::GetAzureBackendInfo)
   , mApzSupportCollector(this, &gfxPlatform::GetApzSupportInfo)
+  , mCompositorBackend(layers::LayersBackend::LAYERS_NONE)
 {
     mAllowDownloadableFonts = UNINITIALIZED_VALUE;
     mFallbackUsesCmaps = UNINITIALIZED_VALUE;
 
     mWordCacheCharLimit = UNINITIALIZED_VALUE;
     mWordCacheMaxEntries = UNINITIALIZED_VALUE;
     mGraphiteShapingEnabled = UNINITIALIZED_VALUE;
     mOpenTypeSVGEnabled = UNINITIALIZED_VALUE;
@@ -405,16 +406,22 @@ gfxPlatform*
 gfxPlatform::GetPlatform()
 {
     if (!gPlatform) {
         Init();
     }
     return gPlatform;
 }
 
+bool
+gfxPlatform::Initialized()
+{
+  return !!gPlatform;
+}
+
 void RecordingPrefChanged(const char *aPrefName, void *aClosure)
 {
   if (Preferences::GetBool("gfx.2d.recording", false)) {
     nsAutoCString fileName;
     nsAdoptingString prefFileName = Preferences::GetString("gfx.2d.recordingfile");
 
     if (prefFileName) {
       fileName.Append(NS_ConvertUTF16toUTF8(prefFileName));
@@ -2439,8 +2446,26 @@ gfxPlatform::GetCompositorBackends(bool 
 {
   if (useAcceleration) {
     GetAcceleratedCompositorBackends(aBackends);
   }
   if (SupportsBasicCompositor()) {
     aBackends.AppendElement(LayersBackend::LAYERS_BASIC);
   }
 }
+
+void
+gfxPlatform::NotifyCompositorCreated(LayersBackend aBackend)
+{
+  if (mCompositorBackend == aBackend) {
+    return;
+  }
+
+  NS_ASSERTION(mCompositorBackend == LayersBackend::LAYERS_NONE, "Compositor backend changed.");
+
+  // Set the backend before we notify so it's available immediately.
+  mCompositorBackend = aBackend;
+
+  // Notify that we created a compositor, so telemetry can update.
+  if (nsCOMPtr<nsIObserverService> obsvc = services::GetObserverService()) {
+    obsvc->NotifyObservers(nullptr, "compositor:created", nullptr);
+  }
+}
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -154,16 +154,22 @@ public:
 
     /**
      * Return a pointer to the current active platform.
      * This is a singleton; it contains mostly convenience
      * functions to obtain platform-specific objects.
      */
     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.
      * Init() arranges for this to be called at an appropriate time.
      */
     static void Shutdown();
 
     static void InitLayersIPC();
@@ -626,16 +632,21 @@ public:
                                  mozilla::gfx::SurfaceFormat aFormat);
 
     /**
      * Wrapper around gfxPrefs::PerfWarnings().
      * Extracted into a function to avoid including gfxPrefs.h from this file.
      */
     static bool PerfWarnings();
 
+    void NotifyCompositorCreated(mozilla::layers::LayersBackend aBackend);
+    mozilla::layers::LayersBackend GetCompositorBackend() const {
+      return mCompositorBackend;
+    }
+
 protected:
     gfxPlatform();
     virtual ~gfxPlatform();
 
     void AppendCJKPrefLangs(eFontPrefLang aPrefLangs[], uint32_t &aLen,
                             eFontPrefLang aCharLang, eFontPrefLang aPageLang);
 
     /**
@@ -746,11 +757,15 @@ private:
     int mTileWidth;
     int mTileHeight;
 
     mozilla::widget::GfxInfoCollector<gfxPlatform> mAzureCanvasBackendCollector;
     mozilla::widget::GfxInfoCollector<gfxPlatform> mApzSupportCollector;
 
     mozilla::RefPtr<mozilla::gfx::DrawEventRecorder> mRecorder;
     mozilla::RefPtr<mozilla::gl::SkiaGLGlue> mSkiaGlue;
+
+    // Backend that we are compositing with. NONE, if no compositor has been
+    // created yet.
+    mozilla::layers::LayersBackend mCompositorBackend;
 };
 
 #endif /* GFX_PLATFORM_H */
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -372,16 +372,18 @@ public:
 
 NS_IMPL_ISUPPORTS(D3D9SharedTextureReporter, nsIMemoryReporter)
 
 gfxWindowsPlatform::gfxWindowsPlatform()
   : mD3D11DeviceInitialized(false)
   , mIsWARP(false)
   , mHasDeviceReset(false)
   , mDoesD3D11TextureSharingWork(false)
+  , mD3D11Status(FeatureStatus::Unused)
+  , mD2DStatus(FeatureStatus::Unused)
 {
     mUseClearTypeForDownloadableFonts = UNINITIALIZED_VALUE;
     mUseClearTypeAlways = UNINITIALIZED_VALUE;
 
     mUsingGDIFonts = false;
 
     /* 
      * Initialize COM 
@@ -431,17 +433,17 @@ bool
 gfxWindowsPlatform::CanUseHardwareVideoDecoding()
 {
     if (!gfxPrefs::LayersPreferD3D9() && !mDoesD3D11TextureSharingWork) {
         return false;
     }
     return !IsWARP() && gfxPlatform::CanUseHardwareVideoDecoding();
 }
 
-void
+FeatureStatus
 gfxWindowsPlatform::InitD2DSupport()
 {
 #ifdef CAIRO_HAS_D2D_SURFACE
   bool d2dBlocked = false;
   nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
   if (gfxInfo) {
     int32_t status;
     if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT2D, &status))) {
@@ -453,40 +455,51 @@ gfxWindowsPlatform::InitD2DSupport()
       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 ((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
 }
 
 void
 gfxWindowsPlatform::InitDWriteSupport()
 {
 #ifdef CAIRO_HAS_DWRITE_FONT
   // Enable when it's preffed on -and- we're using Vista or higher. Or when
@@ -547,17 +560,17 @@ gfxWindowsPlatform::UpdateRenderMode()
       Factory::SetDirect3D11Device(nullptr);
 
       didReset = true;
     }
 
     mRenderMode = RENDER_GDI;
     mUseDirectWrite = gfxPrefs::DirectWriteFontRenderingEnabled();
 
-    InitD2DSupport();
+    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);
@@ -2051,34 +2064,37 @@ gfxWindowsPlatform::InitD3D11Devices()
   // 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); 
+  MOZ_ASSERT(!mD3D11Device);
 
   DriverInitCrashDetection detectCrashes;
   if (InSafeMode() || detectCrashes.DisableAcceleration()) {
+    mD3D11Status = FeatureStatus::Blocked;
     return;
   }
 
   D3D11Status status = CheckD3D11Support();
   if (status == D3D11Status::Blocked) {
+    mD3D11Status = FeatureStatus::Blacklisted;
     return;
   }
 
   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<D3D_FEATURE_LEVEL> featureLevels;
   if (IsWin8OrLater()) {
     featureLevels.AppendElement(D3D_FEATURE_LEVEL_11_1);
   }
   featureLevels.AppendElement(D3D_FEATURE_LEVEL_11_0);
@@ -2092,24 +2108,26 @@ gfxWindowsPlatform::InitD3D11Devices()
     }
   }
 
   if (IsWin8OrLater() &&
       !gfxPrefs::LayersD3D11DisableWARP() &&
       (status == D3D11Status::TryWARP || status == D3D11Status::ForceWARP))
   {
     AttemptWARPDeviceCreation(featureLevels);
+    mD3D11Status = FeatureStatus::Failed;
   }
 
   if (!mD3D11Device) {
     // We could not get a D3D11 compositor, and there's nothing more we can try.
     return;
   }
 
   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
   // default we will let Direct2D use WARP as well.
   if (Factory::SupportsD2D1() && (!mIsWARP || (status == D3D11Status::ForceWARP))) {
     if (!AttemptD3D11ContentDeviceCreation(featureLevels)) {
       mD3D11ContentDevice = nullptr;
       d3d11Module.disown();
@@ -2404,8 +2422,37 @@ gfxWindowsPlatform::GetAcceleratedCompos
     // We don't want D3D9 except on Windows XP
     if (gfxPlatform::CanUseDirect3D9()) {
       aBackends.AppendElement(LayersBackend::LAYERS_D3D9);
     } else {
       NS_WARNING("Direct3D 9-accelerated layers are not supported on this system.");
     }
   }
 }
+
+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();
+}
--- a/gfx/thebes/gfxWindowsPlatform.h
+++ b/gfx/thebes/gfxWindowsPlatform.h
@@ -15,16 +15,17 @@
 
 #include "gfxFontUtils.h"
 #include "gfxWindowsSurface.h"
 #include "gfxFont.h"
 #ifdef CAIRO_HAS_DWRITE_FONT
 #include "gfxDWriteFonts.h"
 #endif
 #include "gfxPlatform.h"
+#include "gfxTelemetry.h"
 #include "gfxTypes.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Atomics.h"
 #include "nsTArray.h"
 #include "nsDataHashtable.h"
 
 #include "mozilla/RefPtr.h"
 
@@ -256,16 +257,28 @@ public:
     bool IsWARP() { return mIsWARP; }
     bool DoesD3D11TextureSharingWork() { return mDoesD3D11TextureSharingWork; }
 
     bool SupportsApzWheelInput() const override {
       return true;
     }
     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<mozilla::gfx::VsyncSource> CreateHardwareVsyncSource() override;
     static mozilla::Atomic<size_t> sD3D11MemoryUsed;
     static mozilla::Atomic<size_t> sD3D9MemoryUsed;
     static mozilla::Atomic<size_t> sD3D9SurfaceImageUsed;
     static mozilla::Atomic<size_t> sD3D9SharedTextureUsed;
 
 protected:
     bool AccelerateLayersByDefault() override {
@@ -293,17 +306,17 @@ private:
     };
     D3D11Status CheckD3D11Support();
     bool AttemptD3D11DeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels);
     bool AttemptWARPDeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels);
     bool AttemptD3D11ImageBridgeDeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels);
     bool AttemptD3D11ContentDeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels);
 
     // Used by UpdateRenderMode().
-    void InitD2DSupport();
+    mozilla::gfx::FeatureStatus InitD2DSupport();
     void InitDWriteSupport();
 
     IDXGIAdapter1 *GetDXGIAdapter();
     bool IsDeviceReset(HRESULT hr, DeviceResetReason* aReason);
 
     bool mUseDirectWrite;
     bool mUsingGDIFonts;
 
@@ -321,12 +334,15 @@ private:
     mozilla::RefPtr<ID3D11Device> mD3D11ImageBridgeDevice;
     bool mD3D11DeviceInitialized;
     mozilla::RefPtr<mozilla::layers::ReadbackManagerD3D11> mD3D11ReadbackManager;
     bool mIsWARP;
     bool mHasDeviceReset;
     bool mDoesD3D11TextureSharingWork;
     DeviceResetReason mDeviceResetReason;
 
+    mozilla::gfx::FeatureStatus mD3D11Status;
+    mozilla::gfx::FeatureStatus mD2DStatus;
+
     virtual void GetPlatformCMSOutputProfile(void* &mem, size_t &size);
 };
 
 #endif /* GFX_WINDOWS_PLATFORM_H */
--- a/widget/GfxInfoBase.cpp
+++ b/widget/GfxInfoBase.cpp
@@ -28,16 +28,17 @@
 #include "nsTArray.h"
 #include "nsXULAppAPI.h"
 #include "nsIXULAppInfo.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Logging.h"
 #include "gfxPrefs.h"
+#include "gfxPlatform.h"
 
 #if defined(MOZ_CRASHREPORTER)
 #include "nsExceptionHandler.h"
 #endif
 
 using namespace mozilla::widget;
 using namespace mozilla;
 using mozilla::MutexAutoLock;
@@ -1174,16 +1175,102 @@ GfxInfoBase::GetMonitors(JSContext* aCx,
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   aResult.setObject(*array);
   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<JS::Value> aOut)
+{
+  JS::Rooted<JSObject*> 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<JSString*> str(aCx, JS_NewStringCopyZ(aCx, backendName));
+    JS::Rooted<JS::Value> 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<JSObject*> aOut)
+{
+}
+
+bool
+GfxInfoBase::InitFeatureObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aContainer,
+                               const char* aName,
+                               mozilla::gfx::FeatureStatus aFeatureStatus,
+                               JS::MutableHandle<JSObject*> aOutObj)
+{
+  JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
+  if (!obj) {
+    return false;
+  }
+
+  const char* status = FeatureStatusToString(aFeatureStatus);
+
+  // Set "status".
+  {
+    JS::Rooted<JSString*> str(aCx, JS_NewStringCopyZ(aCx, status));
+    JS::Rooted<JS::Value> val(aCx, JS::StringValue(str));
+    JS_SetProperty(aCx, obj, "status", val);
+  }
+
+  // Add the feature object to the container.
+  {
+    JS::Rooted<JS::Value> val(aCx, JS::ObjectValue(*obj));
+    JS_SetProperty(aCx, aContainer, aName, val);
+  }
+
+  aOutObj.set(obj);
+  return true;
+}
+
 GfxInfoCollectorBase::GfxInfoCollectorBase()
 {
   GfxInfoBase::AddCollector(this);
 }
 
 GfxInfoCollectorBase::~GfxInfoCollectorBase()
 {
   GfxInfoBase::RemoveCollector(this);
--- a/widget/GfxInfoBase.h
+++ b/widget/GfxInfoBase.h
@@ -14,16 +14,17 @@
 #endif
 #include "nsCOMPtr.h"
 #include "nsIObserver.h"
 #include "nsWeakReference.h"
 #include "GfxDriverInfo.h"
 #include "nsTArray.h"
 #include "nsString.h"
 #include "GfxInfoCollector.h"
+#include "gfxTelemetry.h"
 #include "nsIGfxInfoDebug.h"
 #include "mozilla/Mutex.h"
 #include "js/Value.h"
 #include "mozilla/Attributes.h"
 
 namespace mozilla {
 namespace widget {  
 
@@ -53,16 +54,17 @@ public:
   NS_IMETHOD GetFeatureStatus(int32_t aFeature, int32_t *_retval) override;
   NS_IMETHOD GetFeatureSuggestedDriverVersion(int32_t aFeature, nsAString & _retval) override;
   NS_IMETHOD GetWebGLParameter(const nsAString & aParam, nsAString & _retval) override;
 
   NS_IMETHOD GetMonitors(JSContext* cx, JS::MutableHandleValue _retval) override;
   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<JS::Value>) override;
+  NS_IMETHOD GetFeatures(JSContext*, JS::MutableHandle<JS::Value>) override;
 
   // Initialization function. If you override this, you must call this class's
   // version of Init first.
   // We need Init to be called separately from the constructor so we can
   // register as an observer after all derived classes have been constructed
   // and we know we have a non-zero refcount.
   // Ideally, Init() would be void-return, but the rules of
   // NS_GENERIC_FACTORY_CONSTRUCTOR_INIT require it be nsresult return.
@@ -98,16 +100,24 @@ protected:
                                         nsAString& aSuggestedDriverVersion,
                                         const nsTArray<GfxDriverInfo>& aDriverInfo,
                                         OperatingSystem* aOS = nullptr);
 
   // Gets the driver info table. Used by GfxInfoBase to check for general cases
   // (while subclasses check for more specific ones).
   virtual const nsTArray<GfxDriverInfo>& GetGfxDriverInfo() = 0;
 
+  virtual void DescribeFeatures(JSContext* aCx, JS::Handle<JSObject*> obj);
+  bool InitFeatureObject(
+    JSContext* aCx,
+    JS::Handle<JSObject*> aContainer,
+    const char* aName,
+    mozilla::gfx::FeatureStatus aFeatureStatus,
+    JS::MutableHandle<JSObject*> aOutObj);
+
 private:
   virtual int32_t FindBlocklistedDeviceInList(const nsTArray<GfxDriverInfo>& aDriverInfo,
                                               nsAString& aSuggestedVersion,
                                               int32_t aFeature,
                                               OperatingSystem os);
 
   void EvaluateDownloadedBlacklist(nsTArray<GfxDriverInfo>& aDriverInfo);
 
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -1126,16 +1126,18 @@ void nsBaseWidget::CreateCompositor(int 
   }
 
   lf->SetShadowManager(shadowManager);
   lf->IdentifyTextureHost(textureFactoryIdentifier);
   ImageBridgeChild::IdentifyCompositorTextureHost(textureFactoryIdentifier);
   WindowUsesOMTC();
 
   mLayerManager = lm.forget();
+
+  gfxPlatform::GetPlatform()->NotifyCompositorCreated(mLayerManager->GetCompositorBackendType());
 }
 
 bool nsBaseWidget::ShouldUseOffMainThreadCompositing()
 {
   return gfxPlatform::UsesOffMainThreadCompositing();
 }
 
 LayerManager* nsBaseWidget::GetLayerManager(PLayerTransactionChild* aShadowManager,
--- a/widget/nsIGfxInfo.idl
+++ b/widget/nsIGfxInfo.idl
@@ -3,17 +3,17 @@
  * 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 "nsISupports.idl"
 
 /* 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
 {
   /*
    * These are win32-specific
    */
   readonly attribute boolean D2DEnabled;
   readonly attribute boolean DWriteEnabled;
   readonly attribute DOMString DWriteVersion;
@@ -142,10 +142,36 @@ interface nsIGfxInfo : nsISupports
    */
   DOMString getWebGLParameter(in DOMString aParam);
 
   // only useful on X11
   [noscript, notxpcom] void GetData();
 
   [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".
+  //     "<feature>": {
+  //       // 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": "<status>",
+  //       "version": "<version>",
+  //       "warp": true|false,           // D3D11 only.
+  //       "textureSharing": true|false, // D3D11 only.
+  //     }
+  //   }
+  [implicit_jscontext]
+  jsval getFeatures();
 };
 
--- a/widget/windows/GfxInfo.cpp
+++ b/widget/windows/GfxInfo.cpp
@@ -1250,16 +1250,52 @@ GfxInfo::FindMonitors(JSContext* aCx, JS
     JS_SetProperty(aCx, obj, "pseudoDisplay", pseudoDisplay);
 
     JS::Rooted<JS::Value> element(aCx, JS::ObjectValue(*obj));
     JS_SetElement(aCx, aOutArray, deviceCount++, element);
   }
   return NS_OK;
 }
 
+void
+GfxInfo::DescribeFeatures(JSContext* aCx, JS::Handle<JSObject*> aObj)
+{
+  JS::Rooted<JSObject*> 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<JS::Value> 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<JSString*> str(aCx, JS_NewStringCopyZ(aCx, version));
+    JS::Rooted<JS::Value> val(aCx, JS::StringValue(str));
+    JS_SetProperty(aCx, obj, "version", val);
+  }
+}
+
 #ifdef DEBUG
 
 // Implement nsIGfxInfoDebug
 
 /* void spoofVendorID (in DOMString aVendorID); */
 NS_IMETHODIMP GfxInfo::SpoofVendorID(const nsAString & aVendorID)
 {
   mAdapterVendorID = aVendorID;
--- a/widget/windows/GfxInfo.h
+++ b/widget/windows/GfxInfo.h
@@ -63,16 +63,18 @@ protected:
 
   virtual nsresult GetFeatureStatusImpl(int32_t aFeature, 
                                         int32_t *aStatus, 
                                         nsAString & aSuggestedDriverVersion, 
                                         const nsTArray<GfxDriverInfo>& aDriverInfo, 
                                         OperatingSystem* aOS = nullptr);
   virtual const nsTArray<GfxDriverInfo>& GetGfxDriverInfo();
 
+  void DescribeFeatures(JSContext* cx, JS::Handle<JSObject*> aOut) override;
+
 private:
 
   void AddCrashReportAnnotations();
   void GetCountryCode();
 
   nsString mCountryCode;
   nsString mDeviceString;
   nsString mDeviceID;