Bug 1431013 - Activate powerPreference attribute only when has events; draft
authorDaosheng Mu <daoshengmu@gmail.com>
Wed, 10 Jan 2018 17:39:03 +0800
changeset 721380 f8e87673d215a01242a37babbef1b07ba3552ab4
parent 718845 4db166f0442dddc5b9011c722d7499501fedf283
child 746330 a99e5550b45a94babe326cdf7b3ece789ca00bb7
push id95833
push userbmo:dmu@mozilla.com
push dateWed, 17 Jan 2018 08:49:26 +0000
bugs1431013
milestone59.0a1
Bug 1431013 - Activate powerPreference attribute only when has events; In WebGL spec mention, this `powerPreference` attribute will be ignored if no event listeners exist which would handle both "webglcontextlost" and "webglcontextrestored" events dispatched to the canvas element. MozReview-Commit-ID: EZZysuu4yet
dom/canvas/CanvasRenderingContextHelper.cpp
dom/canvas/CanvasRenderingContextHelper.h
dom/canvas/WebGLContext.cpp
dom/canvas/WebGLContext.h
dom/canvas/nsICanvasRenderingContextInternal.h
dom/html/HTMLCanvasElement.cpp
dom/html/HTMLCanvasElement.h
gfx/gl/GLContextTypes.h
--- a/dom/canvas/CanvasRenderingContextHelper.cpp
+++ b/dom/canvas/CanvasRenderingContextHelper.cpp
@@ -187,16 +187,17 @@ CanvasRenderingContextHelper::GetContext
     CallQueryInterface(context, &cp);
     if (!cp) {
       aRv.Throw(NS_ERROR_FAILURE);
       return nullptr;
     }
 
     mCurrentContext = context.forget();
     mCurrentContextType = contextType;
+    mCurrentContext->SetEventBits(mContextEventBits);
 
     nsresult rv = UpdateContext(aCx, aContextOptions, aRv);
     if (NS_FAILED(rv)) {
       // See bug 645792 and bug 1215072.
       // We want to throw only if dictionary initialization fails,
       // so only in case aRv has been set to some error value.
       if (contextType == CanvasContextType::WebGL1)
         Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_SUCCESS, 0);
--- a/dom/canvas/CanvasRenderingContextHelper.h
+++ b/dom/canvas/CanvasRenderingContextHelper.h
@@ -25,16 +25,23 @@ class EncodeCompleteCallback;
 enum class CanvasContextType : uint8_t {
   NoContext,
   Canvas2D,
   WebGL1,
   WebGL2,
   ImageBitmap
 };
 
+enum class CanvasEventType : uint8_t {
+  NONE = 0,
+  WEBGL_CONTEXT_LOST = 1 << 0,
+  WEBGL_CONTEXT_RESTORED = 1 << 1
+};
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CanvasEventType)
+
 /**
  * Povides common RenderingContext functionality used by both OffscreenCanvas
  * and HTMLCanvasElement.
  */
 class CanvasRenderingContextHelper
 {
 public:
   virtual already_AddRefed<nsISupports>
@@ -68,16 +75,17 @@ protected:
   CreateContext(CanvasContextType aContextType);
 
   already_AddRefed<nsICanvasRenderingContextInternal>
   CreateContextHelper(CanvasContextType aContextType,
                       layers::LayersBackend aCompositorBackend);
 
   virtual nsIntSize GetWidthHeight() = 0;
 
+  CanvasEventType mContextEventBits = CanvasEventType::NONE;
   CanvasContextType mCurrentContextType;
   nsCOMPtr<nsICanvasRenderingContextInternal> mCurrentContext;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // MOZILLA_DOM_CANVASRENDERINGCONTEXTHELPER_H_
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -89,16 +89,22 @@
 
 namespace mozilla {
 
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::gl;
 using namespace mozilla::layers;
 
+#define POWER_PREFERENCE_EVENT(event, power, flags) ((power == dom::WebGLPowerPreference::High_performance) && \
+    (((event) & \
+    (CanvasEventType::WEBGL_CONTEXT_LOST|CanvasEventType::WEBGL_CONTEXT_RESTORED)) == \
+    (CanvasEventType::WEBGL_CONTEXT_LOST|CanvasEventType::WEBGL_CONTEXT_RESTORED)) ? \
+    flags | gl::CreateContextFlags::HIGH_POWER : flags)
+
 WebGLContextOptions::WebGLContextOptions()
     : alpha(true)
     , depth(true)
     , stencil(false)
     , premultipliedAlpha(true)
     , antialias(true)
     , preserveDrawingBuffer(false)
     , failIfMajorPerformanceCaveat(false)
@@ -501,22 +507,27 @@ BaseCaps(const WebGLContextOptions& opti
     return baseCaps;
 }
 
 ////////////////////////////////////////
 
 static already_AddRefed<gl::GLContext>
 CreateGLWithEGL(const gl::SurfaceCaps& caps, gl::CreateContextFlags flags,
                 WebGLContext* webgl,
+                dom::WebGLPowerPreference powerPreference,
                 std::vector<WebGLContext::FailureReason>* const out_failReasons)
 {
     const gfx::IntSize dummySize(16, 16);
     nsCString failureId;
     RefPtr<GLContext> gl = gl::GLContextProviderEGL::CreateOffscreen(dummySize, caps,
-                                                                     flags, &failureId);
+                                                                     POWER_PREFERENCE_EVENT(
+                                                                         webgl->GetEventBits(),
+                                                                         powerPreference,
+                                                                         flags),
+                                                                     &failureId);
     if (gl && gl->IsANGLE()) {
         gl = nullptr;
     }
 
     if (!gl) {
         out_failReasons->push_back(WebGLContext::FailureReason(
             failureId,
             "Error during EGL OpenGL init."
@@ -525,22 +536,27 @@ CreateGLWithEGL(const gl::SurfaceCaps& c
     }
 
     return gl.forget();
 }
 
 static already_AddRefed<GLContext>
 CreateGLWithANGLE(const gl::SurfaceCaps& caps, gl::CreateContextFlags flags,
                   WebGLContext* webgl,
+                  dom::WebGLPowerPreference powerPreference,
                   std::vector<WebGLContext::FailureReason>* const out_failReasons)
 {
     const gfx::IntSize dummySize(16, 16);
     nsCString failureId;
     RefPtr<GLContext> gl = gl::GLContextProviderEGL::CreateOffscreen(dummySize, caps,
-                                                                     flags, &failureId);
+                                                                     POWER_PREFERENCE_EVENT(
+                                                                         webgl->GetEventBits(),
+                                                                         powerPreference,
+                                                                         flags),
+                                                                     &failureId);
     if (gl && !gl->IsANGLE()) {
         gl = nullptr;
     }
 
     if (!gl) {
         out_failReasons->push_back(WebGLContext::FailureReason(
             failureId,
             "Error during ANGLE OpenGL init."
@@ -549,22 +565,27 @@ CreateGLWithANGLE(const gl::SurfaceCaps&
     }
 
     return gl.forget();
 }
 
 static already_AddRefed<gl::GLContext>
 CreateGLWithDefault(const gl::SurfaceCaps& caps, gl::CreateContextFlags flags,
                     WebGLContext* webgl,
+                    dom::WebGLPowerPreference powerPreference,
                     std::vector<WebGLContext::FailureReason>* const out_failReasons)
 {
     const gfx::IntSize dummySize(16, 16);
     nsCString failureId;
     RefPtr<GLContext> gl = gl::GLContextProvider::CreateOffscreen(dummySize, caps,
-                                                                  flags, &failureId);
+                                                                  POWER_PREFERENCE_EVENT(
+                                                                      webgl->GetEventBits(),
+                                                                      powerPreference,
+                                                                      flags),
+                                                                  &failureId);
     if (gl && gl->IsANGLE()) {
         gl = nullptr;
     }
 
     if (!gl) {
         out_failReasons->push_back(WebGLContext::FailureReason(
             failureId,
             "Error during native OpenGL init."
@@ -585,17 +606,18 @@ WebGLContext::CreateAndInitGLWith(FnCrea
 {
     std::queue<gl::SurfaceCaps> fallbackCaps;
     PopulateCapFallbackQueue(baseCaps, &fallbackCaps);
 
     MOZ_RELEASE_ASSERT(!gl, "GFX: Already have a context.");
     RefPtr<gl::GLContext> potentialGL;
     while (!fallbackCaps.empty()) {
         const gl::SurfaceCaps& caps = fallbackCaps.front();
-        potentialGL = fnCreateGL(caps, flags, this, out_failReasons);
+        potentialGL = fnCreateGL(caps, flags, this, mOptions.powerPreference,
+                                 out_failReasons);
         if (potentialGL)
             break;
 
         fallbackCaps.pop();
     }
     if (!potentialGL) {
         out_failReasons->push_back(FailureReason("FEATURE_FAILURE_WEBGL_EXHAUSTED_CAPS",
                                                  "Exhausted GL driver caps."));
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -316,16 +316,17 @@ class WebGLContext
     };
 
     const uint32_t mMaxPerfWarnings;
     mutable uint64_t mNumPerfWarnings;
     const uint32_t mMaxAcceptableFBStatusInvals;
 
     uint64_t mNextFenceId = 1;
     uint64_t mCompletedFenceId = 0;
+    dom::CanvasEventType mEventBits = dom::CanvasEventType::NONE;
 
 public:
     WebGLContext();
 
 protected:
     virtual ~WebGLContext();
 
 public:
@@ -384,16 +385,20 @@ public:
 
     NS_IMETHOD Redraw(const gfxRect&) override {
         return NS_ERROR_NOT_IMPLEMENTED;
     }
 
     void SynthesizeGLError(GLenum err) const;
     void SynthesizeGLError(GLenum err, const char* fmt, ...) const MOZ_FORMAT_PRINTF(3, 4);
 
+    virtual void SetEventBits(dom::CanvasEventType aEventBits) override {
+        mEventBits = aEventBits;
+    }
+
     void ErrorInvalidEnum(const char* fmt = 0, ...) const MOZ_FORMAT_PRINTF(2, 3);
     void ErrorInvalidOperation(const char* fmt = 0, ...) const MOZ_FORMAT_PRINTF(2, 3);
     void ErrorInvalidValue(const char* fmt = 0, ...) const MOZ_FORMAT_PRINTF(2, 3);
     void ErrorInvalidFramebufferOperation(const char* fmt = 0, ...) const MOZ_FORMAT_PRINTF(2, 3);
     void ErrorInvalidEnumInfo(const char* info, GLenum enumValue) const;
     void ErrorInvalidEnumInfo(const char* info, const char* funcName,
                               GLenum enumValue) const;
     void ErrorOutOfMemory(const char* fmt = 0, ...) const MOZ_FORMAT_PRINTF(2, 3);
@@ -489,16 +494,18 @@ public:
     void UpdateContextLossStatus();
     void EnqueueUpdateContextLossStatus();
 
     bool TryToRestoreContext();
 
     void AssertCachedBindings() const;
     void AssertCachedGlobalState() const;
 
+    CanvasEventType GetEventBits() { return mEventBits; }
+
     dom::HTMLCanvasElement* GetCanvas() const { return mCanvasElement; }
     nsIDocument* GetOwnerDoc() const;
 
     // WebIDL WebGLRenderingContext API
     void Commit();
     void GetCanvas(Nullable<dom::OwningHTMLCanvasElementOrOffscreenCanvas>& retval);
 private:
     gfx::IntSize DrawingBufferSize(const char* funcName);
@@ -1592,16 +1599,17 @@ protected:
     bool InitWebGL2(FailureReason* const out_failReason);
 
     bool CreateAndInitGL(bool forceEnabled,
                          std::vector<FailureReason>* const out_failReasons);
 
     typedef already_AddRefed<gl::GLContext> FnCreateGL_T(const gl::SurfaceCaps& caps,
                                                          gl::CreateContextFlags flags,
                                                          WebGLContext* webgl,
+                                                         dom::WebGLPowerPreference powerPreference,
                                                          std::vector<FailureReason>* const out_failReasons);
 
     bool CreateAndInitGLWith(FnCreateGL_T fnCreateGL, const gl::SurfaceCaps& baseCaps,
                              gl::CreateContextFlags flags,
                              std::vector<FailureReason>* const out_failReasons);
 
     void ThrowEvent_WebGLContextCreationError(const nsACString& text);
 
--- a/dom/canvas/nsICanvasRenderingContextInternal.h
+++ b/dom/canvas/nsICanvasRenderingContextInternal.h
@@ -31,16 +31,18 @@ class Layer;
 class LayerManager;
 class WebRenderCanvasData;
 } // namespace layers
 namespace gfx {
 class SourceSurface;
 } // namespace gfx
 } // namespace mozilla
 
+using mozilla::dom::CanvasEventType;
+
 class nsICanvasRenderingContextInternal :
   public nsISupports,
   public nsAPostRefreshObserver
 {
 public:
   typedef mozilla::layers::CanvasLayer CanvasLayer;
   typedef mozilla::layers::CanvasRenderer CanvasRenderer;
   typedef mozilla::layers::Layer Layer;
@@ -175,16 +177,18 @@ public:
 
   // Given a point, return hit region ID if it exists or an empty string if it doesn't
   virtual nsString GetHitRegion(const mozilla::gfx::Point& point) { return nsString(); }
 
   virtual void OnVisibilityChange() {}
 
   virtual void OnMemoryPressure() {}
 
+  virtual void SetEventBits(mozilla::dom::CanvasEventType aEventBits) {}
+
   //
   // shmem support
   //
 
   // If this context can be set to use Mozilla's Shmem segments as its backing
   // store, this will set it to that state. Note that if you have drawn
   // anything into this canvas before changing the shmem state, it will be
   // lost.
--- a/dom/html/HTMLCanvasElement.cpp
+++ b/dom/html/HTMLCanvasElement.cpp
@@ -616,16 +616,35 @@ nsresult HTMLCanvasElement::GetEventTarg
 
       evt->region = mCurrentContext->GetHitRegion(hitpoint);
       aVisitor.mCanHandle = true;
     }
   }
   return nsGenericHTMLElement::GetEventTargetParent(aVisitor);
 }
 
+void
+HTMLCanvasElement::AddEventListener(const nsAString& aType,
+                                    EventListener* aCallback,
+                                    const AddEventListenerOptionsOrBoolean& aOptions,
+                                    const Nullable<bool>& aWantsUntrusted,
+                                    ErrorResult& aRv)
+{
+  if (aType == NS_LITERAL_STRING("webglcontextlost")) {
+    mContextEventBits |= CanvasEventType::WEBGL_CONTEXT_LOST;
+  }
+
+  if (aType == NS_LITERAL_STRING("webglcontextrestored")) {
+    mContextEventBits |= CanvasEventType::WEBGL_CONTEXT_RESTORED;
+  }
+
+  nsGenericHTMLElement::AddEventListener(aType, aCallback, aOptions, aWantsUntrusted,
+                                         aRv);
+}
+
 nsChangeHint
 HTMLCanvasElement::GetAttributeChangeHint(const nsAtom* aAttribute,
                                           int32_t aModType) const
 {
   nsChangeHint retval =
     nsGenericHTMLElement::GetAttributeChangeHint(aAttribute, aModType);
   if (aAttribute == nsGkAtoms::width ||
       aAttribute == nsGkAtoms::height)
--- a/dom/html/HTMLCanvasElement.h
+++ b/dom/html/HTMLCanvasElement.h
@@ -297,16 +297,23 @@ public:
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
                          bool aPreallocateChildren) const override;
   nsresult CopyInnerTo(mozilla::dom::Element* aDest,
                        bool aPreallocateChildren);
 
   virtual nsresult GetEventTargetParent(
                      mozilla::EventChainPreVisitor& aVisitor) override;
 
+  using nsIDOMEventTarget::AddEventListener;
+  virtual void AddEventListener(const nsAString& aType,
+                                EventListener* aCallback,
+                                const AddEventListenerOptionsOrBoolean& aOptions,
+                                const Nullable<bool>& aWantsUntrusted,
+                                ErrorResult& aRv) override;
+
   /*
    * Helpers called by various users of Canvas
    */
 
   already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
                                          Layer *aOldLayer,
                                          LayerManager *aManager);
   bool UpdateWebRenderCanvasData(nsDisplayListBuilder* aBuilder,
--- a/gfx/gl/GLContextTypes.h
+++ b/gfx/gl/GLContextTypes.h
@@ -52,15 +52,17 @@ enum class CreateContextFlags : int8_t {
     FORCE_ENABLE_HARDWARE = 1 << 1,
     /* Don't force discrete GPU to be used (if applicable) */
     ALLOW_OFFLINE_RENDERER =  1 << 2,
     // Ask for ES3 if possible
     PREFER_ES3 = 1 << 3,
 
     NO_VALIDATION = 1 << 4,
     PREFER_ROBUSTNESS = 1 << 5,
+
+    HIGH_POWER = 1 << 6,
 };
 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CreateContextFlags)
 
 } /* namespace gl */
 } /* namespace mozilla */
 
 #endif /* GLCONTEXT_TYPES_H_ */