Bug 1464032 Part 6: Add remote canvas pref and refactor TextuteData creation to use it. r=mattwoodrow
authorBob Owen <bobowencode@gmail.com>
Wed, 28 Nov 2018 20:44:27 +0000
changeset 537449 258c6c1996568b3e7d3ca442a2d87df3f60a4b32
parent 537448 c83dbcc4dade59208e1291208e1fad72543b503d
child 537450 a2720ec3086f5c17bee8ddb394cd43ea0674c2f4
push id11522
push userffxbld-merge
push dateMon, 01 Jul 2019 09:00:55 +0000
treeherdermozilla-beta@53ea74d2bd09 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs1464032
milestone69.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
Bug 1464032 Part 6: Add remote canvas pref and refactor TextuteData creation to use it. r=mattwoodrow This is ground work for when we will be returning a recording TextureData for certain types in subsequent patches.
dom/canvas/CanvasRenderingContext2D.cpp
gfx/config/gfxVars.h
gfx/layers/LayersTypes.h
gfx/layers/PersistentBufferProvider.cpp
gfx/layers/client/ClientLayerManager.cpp
gfx/layers/client/TextureClient.cpp
gfx/layers/client/TextureClient.h
gfx/layers/ipc/LayersMessageUtils.h
gfx/layers/wr/WebRenderLayerManager.cpp
gfx/thebes/gfxPlatform.cpp
modules/libpref/init/StaticPrefList.h
modules/libpref/init/all.js
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -1402,25 +1402,16 @@ bool CanvasRenderingContext2D::TryShared
       (mBufferProvider->GetType() == LayersBackend::LAYERS_CLIENT ||
        mBufferProvider->GetType() == LayersBackend::LAYERS_WR)) {
     // we are already using a shared buffer provider, we are allocating a new
     // one because the current one failed so let's just fall back to the basic
     // provider.
     return false;
   }
 
-#ifdef XP_WIN
-  // Bug 1285271 - Disable shared buffer provider on Windows with D2D due to
-  // instability
-  if (gfxPlatform::GetPlatform()->GetPreferredCanvasBackend() ==
-      BackendType::DIRECT2D1_1) {
-    return false;
-  }
-#endif
-
   RefPtr<LayerManager> layerManager =
       LayerManagerFromCanvasElement(mCanvasElement);
 
   if (!layerManager) {
     return false;
   }
 
   aOutProvider = layerManager->CreatePersistentBufferProvider(
--- a/gfx/config/gfxVars.h
+++ b/gfx/config/gfxVars.h
@@ -45,17 +45,18 @@ class gfxVarReceiver;
   _(UseWebRenderProgramBinaryDisk, bool, false)                    \
   _(WebRenderDebugFlags, int32_t, 0)                               \
   _(ScreenDepth, int32_t, 0)                                       \
   _(GREDirectory, nsString, nsString())                            \
   _(ProfDirectory, nsString, nsString())                           \
   _(UseOMTP, bool, false)                                          \
   _(AllowD3D11KeyedMutex, bool, false)                             \
   _(SystemTextQuality, int32_t, 5 /* CLEARTYPE_QUALITY */)         \
-  _(LayersWindowRecordingPath, nsCString, nsCString())
+  _(LayersWindowRecordingPath, nsCString, nsCString())             \
+  _(RemoteCanvasEnabled, bool, false)                              \
 
 /* Add new entries above this line. */
 
 // Some graphics settings are computed on the UI process and must be
 // communicated to content and GPU processes. gfxVars helps facilitate
 // this. Its function is similar to StaticPrefs, except rather than hold
 // user preferences, it holds dynamically computed values.
 //
--- a/gfx/layers/LayersTypes.h
+++ b/gfx/layers/LayersTypes.h
@@ -150,16 +150,26 @@ enum class LayersBackend : int8_t {
   LAYERS_BASIC,
   LAYERS_OPENGL,
   LAYERS_D3D11,
   LAYERS_CLIENT,
   LAYERS_WR,
   LAYERS_LAST
 };
 
+enum class TextureType : int8_t {
+  Unknown = 0,
+  D3D11,
+  DIB,
+  X11,
+  MacIOSurface,
+  AndroidNativeWindow,
+  Last
+};
+
 enum class BufferMode : int8_t { BUFFER_NONE, BUFFERED };
 
 enum class DrawRegionClip : int8_t { DRAW, NONE };
 
 enum class SurfaceMode : int8_t {
   SURFACE_NONE = 0,
   SURFACE_OPAQUE,
   SURFACE_SINGLE_CHANNEL_ALPHA,
--- a/gfx/layers/PersistentBufferProvider.cpp
+++ b/gfx/layers/PersistentBufferProvider.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "PersistentBufferProvider.h"
 
 #include "Layers.h"
 #include "mozilla/layers/ShadowLayers.h"
 #include "mozilla/layers/TextureClient.h"
 #include "mozilla/gfx/Logging.h"
+#include "mozilla/StaticPrefs.h"
 #include "pratom.h"
 #include "gfxPlatform.h"
 
 namespace mozilla {
 
 using namespace gfx;
 
 namespace layers {
@@ -98,16 +99,31 @@ already_AddRefed<PersistentBufferProvide
 PersistentBufferProviderShared::Create(gfx::IntSize aSize,
                                        gfx::SurfaceFormat aFormat,
                                        KnowsCompositor* aKnowsCompositor) {
   if (!aKnowsCompositor ||
       !aKnowsCompositor->GetTextureForwarder()->IPCOpen()) {
     return nullptr;
   }
 
+  if (!StaticPrefs::PersistentBufferProviderSharedEnabled()) {
+    return nullptr;
+  }
+
+#ifdef XP_WIN
+  // Bug 1285271 - Disable shared buffer provider on Windows with D2D due to
+  // instability, unless we are remoting the canvas drawing to the GPU process.
+  if (gfxPlatform::GetPlatform()->GetPreferredCanvasBackend() ==
+          BackendType::DIRECT2D1_1 &&
+      !TextureData::IsRemote(aKnowsCompositor->GetCompositorBackendType(),
+                             BackendSelector::Canvas)) {
+    return nullptr;
+  }
+#endif
+
   RefPtr<TextureClient> texture = TextureClient::CreateForDrawing(
       aKnowsCompositor, aFormat, aSize, BackendSelector::Canvas,
       TextureFlags::DEFAULT | TextureFlags::NON_BLOCKING_READ_LOCK,
       TextureAllocationFlags::ALLOC_DEFAULT);
 
   if (!texture) {
     return nullptr;
   }
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -857,18 +857,17 @@ void ClientLayerManager::RemoveDidCompos
 
 already_AddRefed<PersistentBufferProvider>
 ClientLayerManager::CreatePersistentBufferProvider(const gfx::IntSize& aSize,
                                                    gfx::SurfaceFormat aFormat) {
   // Don't use a shared buffer provider if compositing is considered "not cheap"
   // because the canvas will most likely be flattened into a thebes layer
   // instead of being sent to the compositor, in which case rendering into
   // shared memory is wasteful.
-  if (IsCompositingCheap() &&
-      StaticPrefs::PersistentBufferProviderSharedEnabled()) {
+  if (IsCompositingCheap()) {
     RefPtr<PersistentBufferProvider> provider =
         PersistentBufferProviderShared::Create(aSize, aFormat,
                                                AsShadowForwarder());
     if (provider) {
       return provider.forget();
     }
   }
 
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -4,16 +4,17 @@
  * 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 "mozilla/layers/TextureClient.h"
 #include <stdint.h>  // for uint8_t, uint32_t, etc
 #include "Layers.h"  // for Layer, etc
 #include "gfx2DGlue.h"
 #include "gfxPlatform.h"  // for gfxPlatform
+#include "MainThreadUtils.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/StaticPrefs.h"
 #include "mozilla/SystemGroup.h"
 #include "mozilla/ipc/SharedMemory.h"  // for SharedMemory, etc
 #include "mozilla/layers/CompositableForwarder.h"
 #include "mozilla/layers/ISurfaceAllocator.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/ImageDataSerializer.h"
@@ -230,16 +231,165 @@ class TextureChild final : PTextureChild
   bool mIPCOpen;
   bool mOwnsTextureData;
   bool mOwnerCalledDestroy;
 
   friend class TextureClient;
   friend void DeallocateTextureClient(TextureDeallocParams params);
 };
 
+static inline gfx::BackendType
+BackendTypeForBackendSelector(LayersBackend aLayersBackend, BackendSelector aSelector)
+{
+  switch (aSelector) {
+    case BackendSelector::Canvas:
+      return gfxPlatform::GetPlatform()->GetPreferredCanvasBackend();
+    case BackendSelector::Content:
+      return gfxPlatform::GetPlatform()->GetContentBackendFor(aLayersBackend);
+    default:
+      MOZ_ASSERT_UNREACHABLE("Unknown backend selector");
+      return gfx::BackendType::NONE;
+  }
+};
+
+static TextureType
+GetTextureType(gfx::SurfaceFormat aFormat, gfx::IntSize aSize,
+               LayersBackend aLayersBackend, gfx::BackendType aBackendType,
+               int32_t aMaxTextureSize, TextureAllocationFlags aAllocFlags)
+{
+#ifdef XP_WIN
+  if ((aLayersBackend == LayersBackend::LAYERS_D3D11 ||
+       aLayersBackend == LayersBackend::LAYERS_WR) &&
+       (aBackendType == gfx::BackendType::DIRECT2D ||
+        aBackendType == gfx::BackendType::DIRECT2D1_1 ||
+        (!!(aAllocFlags & ALLOC_FOR_OUT_OF_BAND_CONTENT) &&
+         DeviceManagerDx::Get()->GetContentDevice())) &&
+      aSize.width <= aMaxTextureSize &&
+      aSize.height <= aMaxTextureSize &&
+      !(aAllocFlags & ALLOC_UPDATE_FROM_SURFACE)) {
+    return TextureType::D3D11;
+  }
+
+  if (aLayersBackend != LayersBackend::LAYERS_WR &&
+      aFormat == SurfaceFormat::B8G8R8X8 &&
+      aBackendType == gfx::BackendType::CAIRO &&
+      NS_IsMainThread()) {
+    return TextureType::DIB;
+  }
+#endif
+
+#ifdef MOZ_X11
+  gfxSurfaceType type =
+    gfxPlatform::GetPlatform()->ScreenReferenceSurface()->GetType();
+
+  if (aLayersBackend == LayersBackend::LAYERS_BASIC &&
+      aBackendType == gfx::BackendType::CAIRO &&
+      type == gfxSurfaceType::Xlib) {
+    return TextureType::X11;
+  }
+  if (aLayersBackend == LayersBackend::LAYERS_OPENGL &&
+      type == gfxSurfaceType::Xlib &&
+      aFormat != SurfaceFormat::A8 &&
+      gl::sGLXLibrary.UseTextureFromPixmap()) {
+    return TextureType::X11;
+  }
+#endif
+
+#ifdef XP_MACOSX
+  if (StaticPrefs::UseIOSurfaceTextures()) {
+    return TextureType::MacIOSurface;
+  }
+#endif
+
+#ifdef MOZ_WIDGET_ANDROID
+  if (StaticPrefs::UseSurfaceTextureTextures()) {
+    return TextureType::AndroidNativeWindow;
+  }
+#endif
+
+  return TextureType::Unknown;
+}
+
+static bool
+ShouldRemoteTextureType(TextureType aTextureType, BackendSelector aSelector)
+{
+  if (!XRE_IsContentProcess()) {
+    return false;
+  }
+
+  if (aSelector != BackendSelector::Canvas || !gfxVars::RemoteCanvasEnabled()) {
+    return false;
+  }
+
+  switch (aTextureType) {
+    case TextureType::D3D11:
+      return true;
+    default:
+      return false;
+  }
+}
+
+/* static */
+TextureData*
+TextureData::Create(TextureForwarder* aAllocator, gfx::SurfaceFormat aFormat,
+                    gfx::IntSize aSize, LayersBackend aLayersBackend,
+                    int32_t aMaxTextureSize, BackendSelector aSelector,
+                    TextureFlags aTextureFlags,
+                    TextureAllocationFlags aAllocFlags)
+{
+  gfx::BackendType moz2DBackend =
+    BackendTypeForBackendSelector(aLayersBackend, aSelector);
+
+  TextureType textureType = GetTextureType(aFormat, aSize, aLayersBackend,
+                                           moz2DBackend, aMaxTextureSize,
+                                           aAllocFlags);
+
+  if (ShouldRemoteTextureType(textureType, aSelector)) {
+    // TODO: return a recording texture data here.
+  }
+
+  switch(textureType) {
+#ifdef XP_WIN
+    case TextureType::D3D11:
+      return D3D11TextureData::Create(aSize, aFormat, aAllocFlags);
+    case TextureType::DIB:
+      return DIBTextureData::Create(aSize, aFormat, aAllocator);
+#endif
+#ifdef MOZ_X11
+    case TextureType::X11:
+      return X11TextureData::Create(aSize, aFormat, aTextureFlags, aAllocator);
+#endif
+#ifdef XP_MACOSX
+    case TextureType::MacIOSurface:
+      return MacIOSurfaceTextureData::Create(aSize, aFormat, moz2DBackend);
+#endif
+#ifdef MOZ_WIDGET_ANDROID
+    case TextureType::AndroidNativeWindow:
+      return AndroidNativeWindowTextureData::Create(aSize, aFormat);
+#endif
+    default:
+      return nullptr;
+  }
+}
+
+/* static */
+bool
+TextureData::IsRemote(LayersBackend aLayersBackend, BackendSelector aSelector)
+{
+  gfx::BackendType moz2DBackend =
+    BackendTypeForBackendSelector(aLayersBackend, aSelector);
+
+  TextureType textureType =
+    GetTextureType(gfx::SurfaceFormat::UNKNOWN, gfx::IntSize(1,1),
+                   aLayersBackend, moz2DBackend, INT32_MAX,
+                   TextureAllocationFlags::ALLOC_DEFAULT);
+
+  return ShouldRemoteTextureType(textureType, aSelector);
+}
+
 static void DestroyTextureData(TextureData* aTextureData,
                                LayersIPCChannel* aAllocator, bool aDeallocate,
                                bool aMainThreadOnly) {
   if (!aTextureData) {
     return;
   }
 
   if (aMainThreadOnly && !NS_IsMainThread()) {
@@ -991,29 +1141,16 @@ bool TextureClient::InitIPDLActor(KnowsC
     LockActor();
   }
 
   return mActor->IPCOpen();
 }
 
 PTextureChild* TextureClient::GetIPDLActor() { return mActor; }
 
-static inline gfx::BackendType BackendTypeForBackendSelector(
-    LayersBackend aLayersBackend, BackendSelector aSelector) {
-  switch (aSelector) {
-    case BackendSelector::Canvas:
-      return gfxPlatform::GetPlatform()->GetPreferredCanvasBackend();
-    case BackendSelector::Content:
-      return gfxPlatform::GetPlatform()->GetContentBackendFor(aLayersBackend);
-    default:
-      MOZ_ASSERT_UNREACHABLE("Unknown backend selector");
-      return gfx::BackendType::NONE;
-  }
-};
-
 // static
 already_AddRefed<TextureClient> TextureClient::CreateForDrawing(
     KnowsCompositor* aAllocator, gfx::SurfaceFormat aFormat, gfx::IntSize aSize,
     BackendSelector aSelector, TextureFlags aTextureFlags,
     TextureAllocationFlags aAllocFlags) {
   LayersBackend layersBackend = aAllocator->GetCompositorBackendType();
   if (aAllocator->SupportsTextureDirectMapping() &&
       std::max(aSize.width, aSize.height) <= aAllocator->GetMaxTextureSize()) {
@@ -1038,63 +1175,19 @@ already_AddRefed<TextureClient> TextureC
   if (!aAllocator || !aAllocator->IPCOpen()) {
     return nullptr;
   }
 
   if (!gfx::Factory::AllowedSurfaceSize(aSize)) {
     return nullptr;
   }
 
-  TextureData* data = nullptr;
-
-#ifdef XP_WIN
-  if ((aLayersBackend == LayersBackend::LAYERS_D3D11 ||
-       aLayersBackend == LayersBackend::LAYERS_WR) &&
-      (moz2DBackend == gfx::BackendType::DIRECT2D ||
-       moz2DBackend == gfx::BackendType::DIRECT2D1_1 ||
-       (!!(aAllocFlags & ALLOC_FOR_OUT_OF_BAND_CONTENT) &&
-        DeviceManagerDx::Get()->GetContentDevice())) &&
-      aSize.width <= aMaxTextureSize && aSize.height <= aMaxTextureSize &&
-      !(aAllocFlags & ALLOC_UPDATE_FROM_SURFACE)) {
-    data = D3D11TextureData::Create(aSize, aFormat, aAllocFlags);
-  }
-
-  if (aLayersBackend != LayersBackend::LAYERS_WR && !data &&
-      aFormat == SurfaceFormat::B8G8R8X8 &&
-      moz2DBackend == gfx::BackendType::CAIRO && NS_IsMainThread()) {
-    data = DIBTextureData::Create(aSize, aFormat, aAllocator);
-  }
-#endif
-
-#ifdef MOZ_X11
-  gfxSurfaceType type =
-      gfxPlatform::GetPlatform()->ScreenReferenceSurface()->GetType();
-
-  if (!data && aLayersBackend == LayersBackend::LAYERS_BASIC &&
-      moz2DBackend == gfx::BackendType::CAIRO && type == gfxSurfaceType::Xlib) {
-    data = X11TextureData::Create(aSize, aFormat, aTextureFlags, aAllocator);
-  }
-  if (!data && aLayersBackend == LayersBackend::LAYERS_OPENGL &&
-      type == gfxSurfaceType::Xlib && aFormat != SurfaceFormat::A8 &&
-      gl::sGLXLibrary.UseTextureFromPixmap()) {
-    data = X11TextureData::Create(aSize, aFormat, aTextureFlags, aAllocator);
-  }
-#endif
-
-#ifdef XP_MACOSX
-  if (!data && StaticPrefs::UseIOSurfaceTextures()) {
-    data = MacIOSurfaceTextureData::Create(aSize, aFormat, moz2DBackend);
-  }
-#endif
-
-#ifdef MOZ_WIDGET_ANDROID
-  if (!data && StaticPrefs::UseSurfaceTextureTextures()) {
-    data = AndroidNativeWindowTextureData::Create(aSize, aFormat);
-  }
-#endif
+  TextureData* data =
+    TextureData::Create(aAllocator, aFormat, aSize, aLayersBackend,
+                        aMaxTextureSize, aSelector, aTextureFlags, aAllocFlags);
 
   if (data) {
     return MakeAndAddRef<TextureClient>(data, aTextureFlags, aAllocator);
   }
 
   // Can't do any better than a buffer texture client.
   return TextureClient::CreateForRawBufferAccess(aAllocator, aFormat, aSize,
                                                  moz2DBackend, aLayersBackend,
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -240,17 +240,26 @@ class TextureData {
         : format(gfx::SurfaceFormat::UNKNOWN),
           hasIntermediateBuffer(false),
           hasSynchronization(false),
           supportsMoz2D(false),
           canExposeMappedData(false),
           canConcurrentlyReadLock(true) {}
   };
 
-  TextureData() { MOZ_COUNT_CTOR(TextureData); }
+  static TextureData* Create(TextureForwarder* aAllocator,
+                             gfx::SurfaceFormat aFormat,
+                             gfx::IntSize aSize,
+                             LayersBackend aLayersBackend,
+                             int32_t aMaxTextureSize,
+                             BackendSelector aSelector,
+                             TextureFlags aTextureFlags,
+                             TextureAllocationFlags aAllocFlags);
+
+  static bool IsRemote(LayersBackend aLayersBackend, BackendSelector aSelector);
 
   virtual ~TextureData() { MOZ_COUNT_DTOR(TextureData); }
 
   virtual void FillInfo(TextureData::Info& aInfo) const = 0;
 
   virtual bool Lock(OpenMode aMode) = 0;
 
   virtual void Unlock() = 0;
@@ -295,16 +304,19 @@ class TextureData {
 #ifdef XP_WIN
   virtual D3D11TextureData* AsD3D11TextureData() { return nullptr; }
   virtual DXGIYCbCrTextureData* AsDXGIYCbCrTextureData() { return nullptr; }
 #endif
 
   virtual BufferTextureData* AsBufferTextureData() { return nullptr; }
 
   virtual GPUVideoTextureData* AsGPUVideoTextureData() { return nullptr; }
+
+protected:
+  TextureData() { MOZ_COUNT_CTOR(TextureData); }
 };
 
 /**
  * TextureClient is a thin abstraction over texture data that need to be shared
  * between the content process and the compositor process. It is the
  * content-side half of a TextureClient/TextureHost pair. A corresponding
  * TextureHost lives on the compositor-side.
  *
--- a/gfx/layers/ipc/LayersMessageUtils.h
+++ b/gfx/layers/ipc/LayersMessageUtils.h
@@ -78,16 +78,23 @@ struct ParamTraits<mozilla::layers::Laye
 template <>
 struct ParamTraits<mozilla::layers::LayersBackend>
     : public ContiguousEnumSerializer<
           mozilla::layers::LayersBackend,
           mozilla::layers::LayersBackend::LAYERS_NONE,
           mozilla::layers::LayersBackend::LAYERS_LAST> {};
 
 template <>
+struct ParamTraits<mozilla::layers::TextureType>
+    : public ContiguousEnumSerializer<
+          mozilla::layers::TextureType,
+          mozilla::layers::TextureType::Unknown,
+          mozilla::layers::TextureType::Last> {};
+
+template <>
 struct ParamTraits<mozilla::layers::ScaleMode>
     : public ContiguousEnumSerializerInclusive<
           mozilla::layers::ScaleMode, mozilla::layers::ScaleMode::SCALE_NONE,
           mozilla::layers::kHighestScaleMode> {};
 
 template <>
 struct ParamTraits<mozilla::StyleScrollSnapStrictness>
     : public ContiguousEnumSerializerInclusive<
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -729,24 +729,23 @@ void WebRenderLayerManager::SetRoot(Laye
 
 already_AddRefed<PersistentBufferProvider>
 WebRenderLayerManager::CreatePersistentBufferProvider(
     const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat) {
   // Ensure devices initialization for canvas 2d. The devices are lazily
   // initialized with WebRender to reduce memory usage.
   gfxPlatform::GetPlatform()->EnsureDevicesInitialized();
 
-  if (StaticPrefs::PersistentBufferProviderSharedEnabled()) {
-    RefPtr<PersistentBufferProvider> provider =
-        PersistentBufferProviderShared::Create(aSize, aFormat,
-                                               AsKnowsCompositor());
-    if (provider) {
-      return provider.forget();
-    }
+  RefPtr<PersistentBufferProvider> provider =
+      PersistentBufferProviderShared::Create(aSize, aFormat,
+                                             AsKnowsCompositor());
+  if (provider) {
+    return provider.forget();
   }
+
   return LayerManager::CreatePersistentBufferProvider(aSize, aFormat);
 }
 
 void WebRenderLayerManager::ClearAsyncAnimations() {
   for (auto& stateManager : mStateManagers) {
     stateManager.ClearAsyncAnimations();
   }
 }
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -2418,16 +2418,19 @@ void gfxPlatform::InitAcceleration() {
 
   sLayersAccelerationPrefsInitialized = true;
 
   if (XRE_IsParentProcess()) {
     Preferences::RegisterCallbackAndCall(
         VideoDecodingFailedChangedCallback,
         "media.hardware-video-decoding.failed");
     InitGPUProcessPrefs();
+
+    gfxVars::SetRemoteCanvasEnabled(StaticPrefs::CanvasRemote() &&
+                                    gfxConfig::IsEnabled(Feature::GPU_PROCESS));
   }
 }
 
 void gfxPlatform::InitGPUProcessPrefs() {
   // We want to hide this from about:support, so only set a default if the
   // pref is known to be true.
   if (!StaticPrefs::GPUProcessEnabled() &&
       !StaticPrefs::GPUProcessForceEnabled()) {
@@ -3389,16 +3392,18 @@ void gfxPlatform::NotifyCompositorCreate
 void gfxPlatform::NotifyGPUProcessDisabled() {
   if (gfxConfig::IsEnabled(Feature::WEBRENDER)) {
     gfxConfig::GetFeature(Feature::WEBRENDER)
         .ForceDisable(
             FeatureStatus::Unavailable, "GPU Process is disabled",
             NS_LITERAL_CSTRING("FEATURE_FAILURE_GPU_PROCESS_DISABLED"));
     gfxVars::SetUseWebRender(false);
   }
+
+  gfxVars::SetRemoteCanvasEnabled(false);
 }
 
 void gfxPlatform::FetchAndImportContentDeviceData() {
   MOZ_ASSERT(XRE_IsContentProcess());
 
   if (gContentDeviceInitData) {
     ImportContentDeviceData(*gContentDeviceInitData);
     return;
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -2522,16 +2522,23 @@ VARCACHE_PREF(
   Live,
   "gfx.canvas.max-size",
   MaxCanvasSize,
   RelaxedAtomicInt32, 0x7fff
 )
 
 VARCACHE_PREF(
   Live,
+  "gfx.canvas.remote",
+  CanvasRemote,
+  RelaxedAtomicBool, false
+)
+
+VARCACHE_PREF(
+  Live,
   "gfx.color_management.enablev4",
   CMSEnableV4,
   RelaxedAtomicBool, false
 )
 
 VARCACHE_PREF(
   Live,
   "gfx.color_management.mode",
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -834,16 +834,17 @@ pref("gfx.font_rendering.directwrite.use
 // Disable antialiasing of Ahem, for use in tests
 pref("gfx.font_ahem_antialias_none", false);
 
 #ifdef XP_WIN
 // comma separated list of backends to use in order of preference
 // e.g., pref("gfx.canvas.azure.backends", "direct2d,skia,cairo");
 pref("gfx.canvas.azure.backends", "direct2d1.1,skia,cairo");
 pref("gfx.content.azure.backends", "direct2d1.1,skia,cairo");
+pref("gfx.canvas.remote", false);
 #else
 #ifdef XP_MACOSX
 pref("gfx.content.azure.backends", "skia");
 pref("gfx.canvas.azure.backends", "skia");
 #else
 pref("gfx.canvas.azure.backends", "skia");
 pref("gfx.content.azure.backends", "skia");
 #endif