Add compositor, layers, and rendering info to nsIGfxInfo. (bug 1179051 part 5, r=mattwoodrow)
☠☠ backed out by 4a0fba2132eb ☠ ☠
authorDavid Anderson <danderson@mozilla.com>
Sun, 19 Jul 2015 14:50:35 -0700
changeset 272601 c4f4027f9f3a4a8b5f58bc08fc8c28968decd20e
parent 272600 c8bd7a3ba0679e77f4382243aa7a7951fa5df09a
child 272602 90446493d402de7d4e9d38a775694881ca594811
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-esr52@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs1179051
milestone42.0a1
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,100 @@ 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";
+    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;