Title: [279172] trunk/Source
Revision
279172
Author
[email protected]
Date
2021-06-23 10:08:51 -0700 (Wed, 23 Jun 2021)

Log Message

rAF driven WebGL submits excessive amount of GPU work when frames are slow
https://bugs.webkit.org/show_bug.cgi?id=227059
<rdar://problem/79385858>

Patch by Kimmo Kinnunen <[email protected]> on 2021-06-23
Reviewed by Kenneth Russell.

Source/ThirdParty/ANGLE:

Advertise GL_ARB_sync for the Metal backend.
Since GL_ARB_sync is core in OpenGL ES 3.0 and the Metal backend advertises OpenGL ES 3.0,
the API must be working already.

* src/libANGLE/renderer/metal/DisplayMtl.mm:
(rx::DisplayMtl::initializeExtensions const):

Source/WebCore:

Limit in-flight WebGL frames to three frames. Do not continue preparation for display
until the commands for the oldest frame have been executed by the GPU.

This limits the impact slow frames have, especially in the
case where the compositor skip frames and WebKit would issue a new slow frame
on top of the skipped frame.

No new tests, tested manually with the pages referenced from the bugs.

* WebCore.xcodeproj/project.pbxproj:
* platform/graphics/angle/GraphicsContextGLANGLE.cpp:
(WebCore::GraphicsContextGLOpenGL::waitAndUpdateOldestFrame):
* platform/graphics/angle/GraphicsContextGLANGLEUtilities.h:
(WebCore::ScopedGLFence::ScopedGLFence):
(WebCore::ScopedGLFence::operator=):
(WebCore::ScopedGLFence::~ScopedGLFence):
(WebCore::ScopedGLFence::reset):
(WebCore::ScopedGLFence::abandon):
(WebCore::ScopedGLFence::fenceSync):
(WebCore::ScopedGLFence::operator GLsync const):
* platform/graphics/cocoa/GraphicsContextGLOpenGLCocoa.mm:
(WebCore::GraphicsContextGLOpenGL::GraphicsContextGLOpenGL):
(WebCore::GraphicsContextGLOpenGL::~GraphicsContextGLOpenGL):
(WebCore::GraphicsContextGLOpenGL::prepareForDisplay):
* platform/graphics/opengl/GraphicsContextGLOpenGL.h:

Modified Paths

Diff

Modified: trunk/Source/ThirdParty/ANGLE/ChangeLog (279171 => 279172)


--- trunk/Source/ThirdParty/ANGLE/ChangeLog	2021-06-23 16:59:34 UTC (rev 279171)
+++ trunk/Source/ThirdParty/ANGLE/ChangeLog	2021-06-23 17:08:51 UTC (rev 279172)
@@ -1,3 +1,18 @@
+2021-06-23  Kimmo Kinnunen  <[email protected]>
+
+        rAF driven WebGL submits excessive amount of GPU work when frames are slow
+        https://bugs.webkit.org/show_bug.cgi?id=227059
+        <rdar://problem/79385858>
+
+        Reviewed by Kenneth Russell.
+
+        Advertise GL_ARB_sync for the Metal backend.
+        Since GL_ARB_sync is core in OpenGL ES 3.0 and the Metal backend advertises OpenGL ES 3.0,
+        the API must be working already.
+
+        * src/libANGLE/renderer/metal/DisplayMtl.mm:
+        (rx::DisplayMtl::initializeExtensions const):
+
 2021-06-22  Dean Jackson  <[email protected]>
 
         [ANGLE] Support importing external MTLTextures

Modified: trunk/Source/ThirdParty/ANGLE/src/libANGLE/renderer/metal/DisplayMtl.mm (279171 => 279172)


--- trunk/Source/ThirdParty/ANGLE/src/libANGLE/renderer/metal/DisplayMtl.mm	2021-06-23 16:59:34 UTC (rev 279171)
+++ trunk/Source/ThirdParty/ANGLE/src/libANGLE/renderer/metal/DisplayMtl.mm	2021-06-23 17:08:51 UTC (rev 279172)
@@ -883,6 +883,9 @@
 
         // GL_OES_EGL_sync
         mNativeExtensions.eglSyncOES = true;
+
+        // GL_ARB_sync
+        mNativeExtensions.glSyncARB = true;
     }
 }
 

Modified: trunk/Source/WebCore/ChangeLog (279171 => 279172)


--- trunk/Source/WebCore/ChangeLog	2021-06-23 16:59:34 UTC (rev 279171)
+++ trunk/Source/WebCore/ChangeLog	2021-06-23 17:08:51 UTC (rev 279172)
@@ -1,3 +1,37 @@
+2021-06-23  Kimmo Kinnunen  <[email protected]>
+
+        rAF driven WebGL submits excessive amount of GPU work when frames are slow
+        https://bugs.webkit.org/show_bug.cgi?id=227059
+        <rdar://problem/79385858>
+
+        Reviewed by Kenneth Russell.
+
+        Limit in-flight WebGL frames to three frames. Do not continue preparation for display
+        until the commands for the oldest frame have been executed by the GPU.
+
+        This limits the impact slow frames have, especially in the
+        case where the compositor skip frames and WebKit would issue a new slow frame
+        on top of the skipped frame.
+
+        No new tests, tested manually with the pages referenced from the bugs.
+
+        * WebCore.xcodeproj/project.pbxproj:
+        * platform/graphics/angle/GraphicsContextGLANGLE.cpp:
+        (WebCore::GraphicsContextGLOpenGL::waitAndUpdateOldestFrame):
+        * platform/graphics/angle/GraphicsContextGLANGLEUtilities.h:
+        (WebCore::ScopedGLFence::ScopedGLFence):
+        (WebCore::ScopedGLFence::operator=):
+        (WebCore::ScopedGLFence::~ScopedGLFence):
+        (WebCore::ScopedGLFence::reset):
+        (WebCore::ScopedGLFence::abandon):
+        (WebCore::ScopedGLFence::fenceSync):
+        (WebCore::ScopedGLFence::operator GLsync const):
+        * platform/graphics/cocoa/GraphicsContextGLOpenGLCocoa.mm:
+        (WebCore::GraphicsContextGLOpenGL::GraphicsContextGLOpenGL):
+        (WebCore::GraphicsContextGLOpenGL::~GraphicsContextGLOpenGL):
+        (WebCore::GraphicsContextGLOpenGL::prepareForDisplay):
+        * platform/graphics/opengl/GraphicsContextGLOpenGL.h:
+
 2021-06-23  Andres Gonzalez  <[email protected]>
 
         Accessibility support for image text recognition.

Modified: trunk/Source/WebCore/Headers.cmake (279171 => 279172)


--- trunk/Source/WebCore/Headers.cmake	2021-06-23 16:59:34 UTC (rev 279171)
+++ trunk/Source/WebCore/Headers.cmake	2021-06-23 17:08:51 UTC (rev 279172)
@@ -1329,6 +1329,7 @@
 
     platform/graphics/angle/ANGLEHeaders.h
     platform/graphics/angle/ExtensionsGLANGLE.h
+    platform/graphics/angle/GraphicsContextGLANGLEUtilities.h
 
     platform/graphics/displaylists/DisplayList.h
     platform/graphics/displaylists/DisplayListDrawGlyphsRecorder.h

Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (279171 => 279172)


--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2021-06-23 16:59:34 UTC (rev 279171)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2021-06-23 17:08:51 UTC (rev 279172)
@@ -714,7 +714,7 @@
 		26F756B51B3B68F20005DD79 /* ImmutableNFANodeBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = 26F756B41B3B68F20005DD79 /* ImmutableNFANodeBuilder.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		26F9A83818A046AC00AEB88A /* ViewportConfiguration.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26F9A83618A046AC00AEB88A /* ViewportConfiguration.cpp */; };
 		26F9A83918A046AC00AEB88A /* ViewportConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 26F9A83718A046AC00AEB88A /* ViewportConfiguration.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		27E3C808257F5E6E00C986AB /* ANGLEHeaders.h in Headers */ = {isa = PBXBuildFile; fileRef = 27E3C806257F5E6E00C986AB /* ANGLEHeaders.h */; };
+		27E3C808257F5E6E00C986AB /* ANGLEHeaders.h in Headers */ = {isa = PBXBuildFile; fileRef = 27E3C806257F5E6E00C986AB /* ANGLEHeaders.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		2914E3081CAB5A440049966F /* AccessibilityAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = 2914E3061CAB5A440049966F /* AccessibilityAttachment.h */; };
 		2936BF5C21D69E4B004A8FC9 /* AccessibilityObjectInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = 2936BF5A21D6999E004A8FC9 /* AccessibilityObjectInterface.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		293EAE1F1356B2FE0067ACF9 /* RuntimeApplicationChecks.h in Headers */ = {isa = PBXBuildFile; fileRef = 293EAE1E1356B2FE0067ACF9 /* RuntimeApplicationChecks.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -2322,7 +2322,7 @@
 		7B6DC81925712E9200380C70 /* GraphicsContextGLIOSurfaceSwapChain.h in Headers */ = {isa = PBXBuildFile; fileRef = 7B6DC81725712E9200380C70 /* GraphicsContextGLIOSurfaceSwapChain.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		7B7311FB25C092B7003B2796 /* ScopedHighPerformanceGPURequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 7B7311FA25C092B7003B2796 /* ScopedHighPerformanceGPURequest.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		7B90417025501142006EEB8C /* RemoteGraphicsContextGLProxyBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 7B90416E25501109006EEB8C /* RemoteGraphicsContextGLProxyBase.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		7BB34A1725345CB200029D08 /* GraphicsContextGLANGLEUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BB34A1625345CB200029D08 /* GraphicsContextGLANGLEUtilities.h */; };
+		7BB34A1725345CB200029D08 /* GraphicsContextGLANGLEUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BB34A1625345CB200029D08 /* GraphicsContextGLANGLEUtilities.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		7BB34A48253776CA00029D08 /* GraphicsContextGLImageExtractor.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BB34A45253776C600029D08 /* GraphicsContextGLImageExtractor.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		7BB680BA25BA1BE4002B8738 /* GraphicsChecksMac.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BB680B825BA1BE4002B8738 /* GraphicsChecksMac.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		7BE7427381FA906FBB4F0F2C /* JSSVGGraphicsElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 950C4C02BED8936F818E2F99 /* JSSVGGraphicsElement.h */; };

Modified: trunk/Source/WebCore/platform/graphics/angle/GraphicsContextGLANGLE.cpp (279171 => 279172)


--- trunk/Source/WebCore/platform/graphics/angle/GraphicsContextGLANGLE.cpp	2021-06-23 16:59:34 UTC (rev 279171)
+++ trunk/Source/WebCore/platform/graphics/angle/GraphicsContextGLANGLE.cpp	2021-06-23 17:08:51 UTC (rev 279172)
@@ -44,6 +44,7 @@
 #include <algorithm>
 #include <cstring>
 #include <wtf/HexNumber.h>
+#include <wtf/Seconds.h>
 #include <wtf/ThreadSpecific.h>
 #include <wtf/text/CString.h>
 #include <wtf/text/StringBuilder.h>
@@ -61,11 +62,8 @@
 
 static const char* packedDepthStencilExtensionName = "GL_OES_packed_depth_stencil";
 
-namespace {
+static Seconds maxFrameDuration = 5_s;
 
-
-} // namespace anonymous
-
 #if PLATFORM(MAC) || PLATFORM(IOS_FAMILY)
 static void wipeAlphaChannelFromPixels(int width, int height, unsigned char* pixels)
 {
@@ -2846,6 +2844,27 @@
 }
 #endif
 
+bool GraphicsContextGLOpenGL::waitAndUpdateOldestFrame()
+{
+    size_t oldestFrameCompletionFence = m_oldestFrameCompletionFence++ % maxPendingFrames;
+    bool success = true;
+    if (ScopedGLFence fence = WTFMove(m_frameCompletionFences[oldestFrameCompletionFence])) {
+        // Wait so that rendering does not get more than maxPendingFrames frames ahead.
+        GLbitfield flags = GL_SYNC_FLUSH_COMMANDS_BIT;
+#if PLATFORM(COCOA)
+        // Avoid using the GL_SYNC_FLUSH_COMMANDS_BIT because each each frame is ended with a flush
+        // due to external IOSurface access. This particular fence is maxPendingFrames behind.
+        // This means the creation of this fence has already been flushed.
+        flags = 0;
+#endif
+        GLenum result = gl::ClientWaitSync(fence, flags, maxFrameDuration.nanosecondsAs<GLuint64>());
+        ASSERT(result != GL_WAIT_FAILED);
+        success = result != GL_WAIT_FAILED && result != GL_TIMEOUT_EXPIRED;
+    }
+    m_frameCompletionFences[oldestFrameCompletionFence].fenceSync();
+    return success;
 }
 
+}
+
 #endif // ENABLE(WEBGL) && USE(ANGLE)

Modified: trunk/Source/WebCore/platform/graphics/angle/GraphicsContextGLANGLEUtilities.h (279171 => 279172)


--- trunk/Source/WebCore/platform/graphics/angle/GraphicsContextGLANGLEUtilities.h	2021-06-23 16:59:34 UTC (rev 279171)
+++ trunk/Source/WebCore/platform/graphics/angle/GraphicsContextGLANGLEUtilities.h	2021-06-23 17:08:51 UTC (rev 279172)
@@ -204,6 +204,42 @@
     GLuint m_object { 0 };
 };
 
+class ScopedGLFence {
+    WTF_MAKE_NONCOPYABLE(ScopedGLFence);
+public:
+    ScopedGLFence() = default;
+    ScopedGLFence(ScopedGLFence&& other)
+        : m_object(std::exchange(other.m_object, { }))
+    {
+    }
+    ~ScopedGLFence() { reset(); }
+    ScopedGLFence& operator=(ScopedGLFence&& other)
+    {
+        if (this != &other) {
+            reset();
+            m_object = std::exchange(other.m_object, { });
+        }
+        return *this;
+    }
+    void reset()
+    {
+        if (m_object) {
+            gl::DeleteSync(m_object);
+            m_object = { };
+        }
+    }
+    void abandon() { m_object = { }; }
+    void fenceSync()
+    {
+        reset();
+        m_object = gl::FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+    }
+    operator GLsync() const { return m_object; }
+    operator bool() const { return m_object; }
+private:
+    GLsync m_object { };
+};
+
 }
 
 #endif

Modified: trunk/Source/WebCore/platform/graphics/cocoa/GraphicsContextGLOpenGLCocoa.mm (279171 => 279172)


--- trunk/Source/WebCore/platform/graphics/cocoa/GraphicsContextGLOpenGLCocoa.mm	2021-06-23 16:59:34 UTC (rev 279171)
+++ trunk/Source/WebCore/platform/graphics/cocoa/GraphicsContextGLOpenGLCocoa.mm	2021-06-23 17:08:51 UTC (rev 279172)
@@ -115,14 +115,11 @@
         displayAttributes.append(EGL_PLATFORM_ANGLE_DEVICE_CONTEXT_VOLATILE_CGL_ANGLE);
         displayAttributes.append(EGL_TRUE);
     }
-    bool canUseMetal = platformSupportsMetal();
-    if (attrs.useMetal && canUseMetal) {
+    if (attrs.useMetal) {
         displayAttributes.append(EGL_PLATFORM_ANGLE_TYPE_ANGLE);
         displayAttributes.append(EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE);
     }
-
-    LOG(WebGL, "Attempting to use ANGLE's %s backend.", attrs.useMetal && canUseMetal ? "Metal" : "OpenGL");
-
+    LOG(WebGL, "Attempting to use ANGLE's %s backend.\n", attrs.useMetal ? "Metal" : "OpenGL");
     displayAttributes.append(EGL_NONE);
     display = EGL_GetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, reinterpret_cast<void*>(EGL_DEFAULT_DISPLAY), displayAttributes.data());
 
@@ -200,6 +197,10 @@
     : GraphicsContextGL(attrs, sharedContext)
 {
     m_isForWebGL2 = attrs.webGLVersion == GraphicsContextGLWebGLVersion::WebGL2;
+    if (attrs.useMetal && !platformSupportsMetal()) {
+        attrs.useMetal = false;
+        setContextAttributes(attrs);
+    }
 
     m_displayObj = InitializeEGLDisplay(attrs);
     if (!m_displayObj)
@@ -286,6 +287,12 @@
     if (m_isForWebGL2) {
         // For WebGL 2.0 occlusion queries to work.
         requiredExtensions.append("GL_EXT_occlusion_query_boolean"_s);
+    } else {
+        if (contextAttributes().useMetal) {
+            // The implementation uses GLsync objects. Enable the functionality for WebGL 1.0 contexts
+            // that use OpenGL ES 2.0.
+            requiredExtensions.append("GL_ARB_sync"_s);
+        }
     }
 #if PLATFORM(MAC) || PLATFORM(MACCATALYST)
     if (!needsEAGLOnMac()) {
@@ -293,8 +300,7 @@
         requiredExtensions.append("GL_ANGLE_texture_rectangle"_s);
         // For creating the EGL surface from an IOSurface.
         requiredExtensions.append("GL_EXT_texture_format_BGRA8888"_s);
-            }
-
+    }
 #endif // PLATFORM(MAC) || PLATFORM(MACCATALYST)
     ExtensionsGL& extensions = getExtensions();
     for (auto& extension : requiredExtensions) {
@@ -385,6 +391,11 @@
             gl::DeleteTextures(1, &m_preserveDrawingBufferTexture);
         if (m_preserveDrawingBufferFBO)
             gl::DeleteFramebuffers(1, &m_preserveDrawingBufferFBO);
+        for (auto& fence : m_frameCompletionFences)
+            fence.reset();
+    } else {
+        for (auto& fence : m_frameCompletionFences)
+            fence.abandon();
     }
     if (m_displayBufferPbuffer) {
         EGL_DestroySurface(m_displayObj, m_displayBufferPbuffer);
@@ -720,8 +731,14 @@
     // Error will be handled by next call to makeContextCurrent() which will notice lack of display buffer.
     if (!hasNewBacking)
         allocateAndBindDisplayBufferBacking();
+    markLayerComposited();
 
-    markLayerComposited();
+    if (contextAttributes().useMetal) {
+        // OpenGL sync objects are not signaling upon completion on Catalina-era drivers.
+        // OpenGL drivers typically implement some sort of internal throttling.
+        bool success = waitAndUpdateOldestFrame();
+        UNUSED_VARIABLE(success); // FIXME: implement context lost.
+    }
 }
 
 std::optional<PixelBuffer> GraphicsContextGLOpenGL::readCompositedResults()

Modified: trunk/Source/WebCore/platform/graphics/opengl/GraphicsContextGLOpenGL.h (279171 => 279172)


--- trunk/Source/WebCore/platform/graphics/opengl/GraphicsContextGLOpenGL.h	2021-06-23 16:59:34 UTC (rev 279171)
+++ trunk/Source/WebCore/platform/graphics/opengl/GraphicsContextGLOpenGL.h	2021-06-23 17:08:51 UTC (rev 279172)
@@ -45,11 +45,17 @@
 #include "PlatformCALayer.h"
 #endif
 
-#if !USE(ANGLE)
+#if USE(ANGLE)
+#include "GraphicsContextGLANGLEUtilities.h"
+#else
 #include "ANGLEWebKitBridge.h"
 #include "ExtensionsGLOpenGLCommon.h"
 #endif
 
+#if PLATFORM(MAC)
+#include "ScopedHighPerformanceGPURequest.h"
+#endif
+
 // FIXME: Find a better way to avoid the name confliction for NO_ERROR.
 #if PLATFORM(WIN)
 #undef NO_ERROR
@@ -72,10 +78,6 @@
 }
 #endif
 
-#if PLATFORM(MAC)
-#include "ScopedHighPerformanceGPURequest.h"
-#endif
-
 namespace WebCore {
 class ExtensionsGL;
 #if USE(ANGLE)
@@ -574,6 +576,10 @@
     bool allocateAndBindDisplayBufferBacking();
     bool bindDisplayBufferBacking(std::unique_ptr<IOSurface> backing, void* pbuffer);
 #endif
+#if USE(ANGLE)
+    // Returns false if context should be lost due to timeout.
+    bool waitAndUpdateOldestFrame() WARN_UNUSED_RETURN;
+#endif
 
 #if PLATFORM(COCOA)
     GraphicsContextGLIOSurfaceSwapChain* m_swapChain { nullptr };
@@ -787,6 +793,11 @@
 #if ENABLE(VIDEO) && USE(AVFOUNDATION)
     std::unique_ptr<GraphicsContextGLCV> m_cv;
 #endif
+#if USE(ANGLE)
+    static constexpr size_t maxPendingFrames = 3;
+    size_t m_oldestFrameCompletionFence { 0 };
+    ScopedGLFence m_frameCompletionFences[maxPendingFrames];
+#endif
 };
 
 } // namespace WebCore
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to