Bug 1349799 - Implement WebGLPowerPreference and gl::CreateContextFlags::HIGH_POWER. - r=kvark draft
authorJeff Gilbert <jgilbert@mozilla.com>
Fri, 22 Jun 2018 16:47:02 -0700
changeset 810394 8cdb2bea052827f6e4764d6161e5aa9427d69218
parent 809776 2211ebb43ed90fdb3641602da83332d4d46949e9
push id113972
push userbmo:jgilbert@mozilla.com
push dateMon, 25 Jun 2018 19:38:51 +0000
reviewerskvark
bugs1349799
milestone62.0a1
Bug 1349799 - Implement WebGLPowerPreference and gl::CreateContextFlags::HIGH_POWER. - r=kvark Based on patches by :daoshengmu. MozReview-Commit-ID: FSbJV8DLyJ4
dom/canvas/WebGLContext.cpp
dom/canvas/WebGLContext.h
gfx/gl/GLContextProviderCGL.mm
gfx/gl/GLContextTypes.h
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -89,29 +89,37 @@
 namespace mozilla {
 
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::gl;
 using namespace mozilla::layers;
 
 WebGLContextOptions::WebGLContextOptions()
-    : alpha(true)
-    , depth(true)
-    , stencil(false)
-    , premultipliedAlpha(true)
-    , antialias(true)
-    , preserveDrawingBuffer(false)
-    , failIfMajorPerformanceCaveat(false)
 {
     // Set default alpha state based on preference.
     if (gfxPrefs::WebGLDefaultNoAlpha())
         alpha = false;
 }
 
+bool
+WebGLContextOptions::operator==(const WebGLContextOptions& r) const
+{
+    bool eq = true;
+    eq &= (alpha == r.alpha);
+    eq &= (depth == r.depth);
+    eq &= (stencil == r.stencil);
+    eq &= (premultipliedAlpha == r.premultipliedAlpha);
+    eq &= (antialias == r.antialias);
+    eq &= (preserveDrawingBuffer == r.preserveDrawingBuffer);
+    eq &= (failIfMajorPerformanceCaveat == r.failIfMajorPerformanceCaveat);
+    eq &= (powerPreference == r.powerPreference);
+    return eq;
+}
+
 WebGLContext::WebGLContext()
     : WebGLContextUnchecked(nullptr)
     , mMaxPerfWarnings(gfxPrefs::WebGLMaxPerfWarnings())
     , mNumPerfWarnings(0)
     , mMaxAcceptableFBStatusInvals(gfxPrefs::WebGLMaxAcceptableFBStatusInvals())
     , mDataAllocGLCallCount(0)
     , mBypassShaderValidation(false)
     , mEmptyTFO(0)
@@ -363,16 +371,17 @@ WebGLContext::SetContextOptions(JSContex
     WebGLContextOptions newOpts;
 
     newOpts.stencil = attributes.mStencil;
     newOpts.depth = attributes.mDepth;
     newOpts.premultipliedAlpha = attributes.mPremultipliedAlpha;
     newOpts.antialias = attributes.mAntialias;
     newOpts.preserveDrawingBuffer = attributes.mPreserveDrawingBuffer;
     newOpts.failIfMajorPerformanceCaveat = attributes.mFailIfMajorPerformanceCaveat;
+    newOpts.powerPreference = attributes.mPowerPreference;
 
     if (attributes.mAlpha.WasPassed())
         newOpts.alpha = attributes.mAlpha.Value();
 
     // Don't do antialiasing if we've disabled MSAA.
     if (!gfxPrefs::MSAALevel())
       newOpts.antialias = false;
 
@@ -381,17 +390,17 @@ WebGLContext::SetContextOptions(JSContex
                newOpts.antialias ? 1 : 0,
                newOpts.stencil ? 1 : 0,
                newOpts.depth ? 1 : 0,
                newOpts.alpha ? 1 : 0,
                newOpts.premultipliedAlpha ? 1 : 0,
                newOpts.preserveDrawingBuffer ? 1 : 0);
 #endif
 
-    if (mOptionsFrozen && newOpts != mOptions) {
+    if (mOptionsFrozen && !(newOpts == mOptions)) {
         // Error if the options are already frozen, and the ones that were asked for
         // aren't the same as what they were originally.
         return NS_ERROR_FAILURE;
     }
 
     mOptions = newOpts;
     return NS_OK;
 }
@@ -662,16 +671,32 @@ WebGLContext::CreateAndInitGL(bool force
     }
 
     if (IsWebGL2()) {
         flags |= gl::CreateContextFlags::PREFER_ES3;
     } else if (!gfxPrefs::WebGL1AllowCoreProfile()) {
         flags |= gl::CreateContextFlags::REQUIRE_COMPAT_PROFILE;
     }
 
+    switch (mOptions.powerPreference) {
+    case dom::WebGLPowerPreference::Default:
+        // Eventually add a heuristic, but for now default to high-performance.
+        // We can even make it dynamic by holding on to a ForceDiscreteGPUHelperCGL iff
+        // we decide it's a high-performance application:
+        // - Non-trivial canvas size
+        // - Many draw calls
+        // - Same origin with root page (try to stem bleeding from WebGL ads/trackers)
+    case dom::WebGLPowerPreference::High_performance:
+        flags |= gl::CreateContextFlags::HIGH_POWER;
+        break;
+
+    case dom::WebGLPowerPreference::Low_power:
+        break;
+    }
+
 #ifdef XP_MACOSX
     const nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
     nsString vendorID, deviceID;
 
     // Avoid crash for Intel HD Graphics 3000 on OSX. (Bug 1413269)
     gfxInfo->GetAdapterVendorID(vendorID);
     gfxInfo->GetAdapterDeviceID(deviceID);
     if (vendorID.EqualsLiteral("0x8086") &&
@@ -1405,16 +1430,17 @@ WebGLContext::GetContextAttributes(dom::
 
     result.mAlpha.Construct(mOptions.alpha);
     result.mDepth = mOptions.depth;
     result.mStencil = mOptions.stencil;
     result.mAntialias = mOptions.antialias;
     result.mPremultipliedAlpha = mOptions.premultipliedAlpha;
     result.mPreserveDrawingBuffer = mOptions.preserveDrawingBuffer;
     result.mFailIfMajorPerformanceCaveat = mOptions.failIfMajorPerformanceCaveat;
+    result.mPowerPreference = mOptions.powerPreference;
 }
 
 void
 WebGLContext::ForceClearFramebufferWithDefaultValues(const GLbitfield clearBits,
                                                      const bool fakeNoAlpha) const
 {
     const bool initializeColorBuffer = bool(clearBits & LOCAL_GL_COLOR_BUFFER_BIT);
     const bool initializeDepthBuffer = bool(clearBits & LOCAL_GL_DEPTH_BUFFER_BIT);
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -25,20 +25,16 @@
 #include "nsICanvasRenderingContextInternal.h"
 #include "nsLayoutUtils.h"
 #include "nsTArray.h"
 #include "nsWrapperCache.h"
 #include "SurfaceTypes.h"
 #include "ScopedGLHelpers.h"
 #include "TexUnpackBlob.h"
 
-#ifdef XP_MACOSX
-#include "ForceDiscreteGPUHelperCGL.h"
-#endif
-
 // Local
 #include "CacheMap.h"
 #include "WebGLContextLossHandler.h"
 #include "WebGLContextUnchecked.h"
 #include "WebGLFormats.h"
 #include "WebGLObjectModel.h"
 #include "WebGLStrongTypes.h"
 #include "WebGLTexture.h"
@@ -112,40 +108,27 @@ struct UniformBlockInfo;
 } // namespace webgl
 
 WebGLTexelFormat GetWebGLTexelFormat(TexInternalFormat format);
 
 void AssertUintParamCorrect(gl::GLContext* gl, GLenum pname, GLuint shadow);
 
 struct WebGLContextOptions
 {
-    // these are defaults
-    WebGLContextOptions();
+    bool alpha = true;
+    bool depth = true;
+    bool stencil = false;
+    bool premultipliedAlpha = true;
+    bool antialias = true;
+    bool preserveDrawingBuffer = false;
+    bool failIfMajorPerformanceCaveat = false;
+    dom::WebGLPowerPreference powerPreference = dom::WebGLPowerPreference::Default;
 
-    bool operator==(const WebGLContextOptions& other) const {
-        return
-            alpha == other.alpha &&
-            depth == other.depth &&
-            stencil == other.stencil &&
-            premultipliedAlpha == other.premultipliedAlpha &&
-            antialias == other.antialias &&
-            preserveDrawingBuffer == other.preserveDrawingBuffer;
-    }
-
-    bool operator!=(const WebGLContextOptions& other) const {
-        return !operator==(other);
-    }
-
-    bool alpha;
-    bool depth;
-    bool stencil;
-    bool premultipliedAlpha;
-    bool antialias;
-    bool preserveDrawingBuffer;
-    bool failIfMajorPerformanceCaveat;
+    WebGLContextOptions();
+    bool operator==(const WebGLContextOptions&) const;
 };
 
 // From WebGLContextUtils
 TexTarget TexImageTargetToTexTarget(TexImageTarget texImageTarget);
 
 struct WebGLIntOrFloat {
     const enum {
         Int,
@@ -2036,26 +2019,16 @@ public:
     void UpdateLastUseIndex();
 
     template <typename WebGLObjectType>
     JS::Value WebGLObjectAsJSValue(JSContext* cx, const WebGLObjectType*,
                                    ErrorResult& rv) const;
     template <typename WebGLObjectType>
     JSObject* WebGLObjectAsJSObject(JSContext* cx, const WebGLObjectType*,
                                     ErrorResult& rv) const;
-
-#ifdef XP_MACOSX
-    // see bug 713305. This RAII helper guarantees that we're on the discrete GPU, during its lifetime
-    // Debouncing note: we don't want to switch GPUs too frequently, so try to not create and destroy
-    // these objects at high frequency. Having WebGLContext's hold one such object seems fine,
-    // because WebGLContext objects only go away during GC, which shouldn't happen too frequently.
-    // If in the future GC becomes much more frequent, we may have to revisit then (maybe use a timer).
-    ForceDiscreteGPUHelperCGL mForceDiscreteGPUHelper;
-#endif
-
 public:
     // console logging helpers
     void GenerateWarning(const char* fmt, ...) const MOZ_FORMAT_PRINTF(2, 3);
     void GenerateWarning(const char* fmt, va_list ap) const MOZ_FORMAT_PRINTF(2, 0);
 
     void GeneratePerfWarning(const char* fmt, ...) const MOZ_FORMAT_PRINTF(2, 3);
 
 public:
--- a/gfx/gl/GLContextProviderCGL.mm
+++ b/gfx/gl/GLContextProviderCGL.mm
@@ -201,36 +201,16 @@ static const NSOpenGLPixelFormatAttribut
     NSOpenGLPFAAllowOfflineRenderers,
     NSOpenGLPFADoubleBuffer,
     NSOpenGLPFAOpenGLProfile,
     NSOpenGLProfileVersion3_2Core,
     NSOpenGLPFADepthSize, 24,
     0
 };
 
-static const NSOpenGLPixelFormatAttribute kAttribs_offscreen[] = {
-    0
-};
-
-static const NSOpenGLPixelFormatAttribute kAttribs_offscreen_allow_offline[] = {
-    NSOpenGLPFAAllowOfflineRenderers,
-    0
-};
-
-static const NSOpenGLPixelFormatAttribute kAttribs_offscreen_accel[] = {
-    NSOpenGLPFAAccelerated,
-    0
-};
-
-static const NSOpenGLPixelFormatAttribute kAttribs_offscreen_coreProfile[] = {
-    NSOpenGLPFAAccelerated,
-    NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
-    0
-};
-
 static NSOpenGLContext*
 CreateWithFormat(const NSOpenGLPixelFormatAttribute* attribs)
 {
     NSOpenGLPixelFormat* format = [[NSOpenGLPixelFormat alloc]
                                    initWithAttributes:attribs];
     if (!format) {
         NS_WARNING("Failed to create NSOpenGLPixelFormat.");
         return nullptr;
@@ -302,33 +282,43 @@ static already_AddRefed<GLContextCGL>
 CreateOffscreenFBOContext(CreateContextFlags flags)
 {
     if (!sCGLLibrary.EnsureInitialized()) {
         return nullptr;
     }
 
     NSOpenGLContext* context = nullptr;
 
+    std::vector<NSOpenGLPixelFormatAttribute> attribs;
+
+    if (flags & CreateContextFlags::ALLOW_OFFLINE_RENDERER ||
+        !(flags & CreateContextFlags::HIGH_POWER))
+    {
+        // This is really poorly named on Apple's part, but "AllowOfflineRenderers" means
+        // that we want to allow running on the iGPU instead of requiring the dGPU.
+        attribs.push_back(NSOpenGLPFAAllowOfflineRenderers);
+    }
+
+    if (gfxPrefs::RequireHardwareGL()) {
+        attribs.push_back(NSOpenGLPFAAccelerated);
+    }
+
     if (!(flags & CreateContextFlags::REQUIRE_COMPAT_PROFILE)) {
-        context = CreateWithFormat(kAttribs_offscreen_coreProfile);
+        auto coreAttribs = attribs;
+        coreAttribs.push_back(NSOpenGLPFAOpenGLProfile);
+        coreAttribs.push_back(NSOpenGLProfileVersion3_2Core);
+        coreAttribs.push_back(0);
+        context = CreateWithFormat(coreAttribs.data());
     }
+
     if (!context) {
-        if (flags & CreateContextFlags::ALLOW_OFFLINE_RENDERER) {
-          if (gfxPrefs::RequireHardwareGL())
-              context = CreateWithFormat(kAttribs_singleBuffered);
-          else
-              context = CreateWithFormat(kAttribs_offscreen_allow_offline);
+        attribs.push_back(0);
+        context = CreateWithFormat(attribs.data());
+    }
 
-        } else {
-          if (gfxPrefs::RequireHardwareGL())
-              context = CreateWithFormat(kAttribs_offscreen_accel);
-          else
-              context = CreateWithFormat(kAttribs_offscreen);
-        }
-    }
     if (!context) {
         NS_WARNING("Failed to create NSOpenGLContext.");
         return nullptr;
     }
 
     RefPtr<GLContextCGL> glContext = new GLContextCGL(flags, SurfaceCaps::Any(), context,
                                                       true);
 
--- a/gfx/gl/GLContextTypes.h
+++ b/gfx/gl/GLContextTypes.h
@@ -40,27 +40,29 @@ struct GLFormats
 
     GLenum depthStencil;
     GLenum depth;
     GLenum stencil;
 
     GLsizei samples;
 };
 
-enum class CreateContextFlags : int8_t {
+enum class CreateContextFlags : uint8_t {
     NONE = 0,
     REQUIRE_COMPAT_PROFILE = 1 << 0,
     // Force the use of hardware backed GL, don't allow software implementations.
     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_ */