Title: [286160] trunk
Revision
286160
Author
[email protected]
Date
2021-11-25 01:17:45 -0800 (Thu, 25 Nov 2021)

Log Message

ANGLE Metal: The memory backing IOSurfaces of former client buffer pbuffers is leaked
https://bugs.webkit.org/show_bug.cgi?id=233328
<rdar://problem/85563187>

Patch by Kimmo Kinnunen <[email protected]> on 2021-11-25
Reviewed by Antti Koivisto.

Source/WebCore:

Fix a bug where recycling GraphicsContextGLOpenGL display buffers would
leak the spare buffer pbuffer handle. This would happen if the
display buffer was marked as "in use", so that the IOSurface reference
would be dropped immediately in order to not use it as next drawing buffer.
However, the IOSurface is still bound in ANGLE and as such, the pbuffer handle
must be returned during `GraphicsContextGLIOSurfaceSwapChain::recycleBuffer``
call.

Adds API tests for testing the leak.

* platform/graphics/cocoa/GraphicsContextGLIOSurfaceSwapChain.cpp:
(WebCore::GraphicsContextGLIOSurfaceSwapChain::recycleBuffer):
(WebCore::GraphicsContextGLIOSurfaceSwapChain::present):

Source/WebCore/PAL:

Add prototype for IOSurfaceIncrementUseCount.
Currently used for a test, in simulating CA behavior.

* pal/spi/cocoa/IOSurfaceSPI.h:

Tools:

Add tests testing Cocoa GraphicsContextGLOpenGL drawing buffer
recycling behavior.

* TestWebKitAPI/Configurations/TestWebKitAPI.xcconfig:
* TestWebKitAPI/Sources.txt:
* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebCore/ImageBufferTests.cpp:
(TestWebKitAPI::imageBufferPixelIs):
(TestWebKitAPI::memoryFootprintChangedBy): Deleted.
* TestWebKitAPI/Tests/WebCore/cocoa/TestGraphicsContextGLOpenGLCocoa.mm:
(TestWebKitAPI::createDefaultTestContext):
(TestWebKitAPI::changeContextContents):
(TestWebKitAPI::TEST):
* TestWebKitAPI/TestUtilities.h: Added.
* TestWebKitAPI/TestUtilities.cpp: Added.
Add few useful functions to TestUtilities.h so that different
tests can use them.

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (286159 => 286160)


--- trunk/Source/WebCore/ChangeLog	2021-11-25 06:59:55 UTC (rev 286159)
+++ trunk/Source/WebCore/ChangeLog	2021-11-25 09:17:45 UTC (rev 286160)
@@ -1,3 +1,25 @@
+2021-11-25  Kimmo Kinnunen  <[email protected]>
+
+        ANGLE Metal: The memory backing IOSurfaces of former client buffer pbuffers is leaked
+        https://bugs.webkit.org/show_bug.cgi?id=233328
+        <rdar://problem/85563187>
+
+        Reviewed by Antti Koivisto.
+
+        Fix a bug where recycling GraphicsContextGLOpenGL display buffers would
+        leak the spare buffer pbuffer handle. This would happen if the
+        display buffer was marked as "in use", so that the IOSurface reference
+        would be dropped immediately in order to not use it as next drawing buffer.
+        However, the IOSurface is still bound in ANGLE and as such, the pbuffer handle
+        must be returned during `GraphicsContextGLIOSurfaceSwapChain::recycleBuffer``
+        call.
+
+        Adds API tests for testing the leak.
+
+        * platform/graphics/cocoa/GraphicsContextGLIOSurfaceSwapChain.cpp:
+        (WebCore::GraphicsContextGLIOSurfaceSwapChain::recycleBuffer):
+        (WebCore::GraphicsContextGLIOSurfaceSwapChain::present):
+
 2021-11-24  David Kilzer  <[email protected]>
 
         Compiler should be able to check localized format strings for consistency

Modified: trunk/Source/WebCore/PAL/ChangeLog (286159 => 286160)


--- trunk/Source/WebCore/PAL/ChangeLog	2021-11-25 06:59:55 UTC (rev 286159)
+++ trunk/Source/WebCore/PAL/ChangeLog	2021-11-25 09:17:45 UTC (rev 286160)
@@ -1,3 +1,16 @@
+2021-11-25  Kimmo Kinnunen  <[email protected]>
+
+        ANGLE Metal: The memory backing IOSurfaces of former client buffer pbuffers is leaked
+        https://bugs.webkit.org/show_bug.cgi?id=233328
+        <rdar://problem/85563187>
+
+        Reviewed by Antti Koivisto.
+
+        Add prototype for IOSurfaceIncrementUseCount.
+        Currently used for a test, in simulating CA behavior.
+
+        * pal/spi/cocoa/IOSurfaceSPI.h:
+
 2021-11-22  Myles C. Maxfield  <[email protected]>
 
         [WebGPU] Use OptionSet where it makes sense to

Modified: trunk/Source/WebCore/PAL/pal/spi/cocoa/IOSurfaceSPI.h (286159 => 286160)


--- trunk/Source/WebCore/PAL/pal/spi/cocoa/IOSurfaceSPI.h	2021-11-25 06:59:55 UTC (rev 286159)
+++ trunk/Source/WebCore/PAL/pal/spi/cocoa/IOSurfaceSPI.h	2021-11-25 09:17:45 UTC (rev 286160)
@@ -71,6 +71,7 @@
 size_t IOSurfaceGetPropertyMaximum(CFStringRef property);
 size_t IOSurfaceGetWidth(IOSurfaceRef buffer);
 OSType IOSurfaceGetPixelFormat(IOSurfaceRef buffer);
+void IOSurfaceIncrementUseCount(IOSurfaceRef buffer);
 Boolean IOSurfaceIsInUse(IOSurfaceRef buffer);
 IOReturn IOSurfaceLock(IOSurfaceRef buffer, uint32_t options, uint32_t *seed);
 IOSurfaceRef IOSurfaceLookupFromMachPort(mach_port_t);

Modified: trunk/Source/WebCore/platform/graphics/cocoa/GraphicsContextGLIOSurfaceSwapChain.cpp (286159 => 286160)


--- trunk/Source/WebCore/platform/graphics/cocoa/GraphicsContextGLIOSurfaceSwapChain.cpp	2021-11-25 06:59:55 UTC (rev 286159)
+++ trunk/Source/WebCore/platform/graphics/cocoa/GraphicsContextGLIOSurfaceSwapChain.cpp	2021-11-25 09:17:45 UTC (rev 286160)
@@ -47,9 +47,8 @@
     if (m_spareBuffer.surface) {
         if (m_spareBuffer.surface->isInUse())
             m_spareBuffer.surface.reset();
-        return WTFMove(m_spareBuffer);
     }
-    return { };
+    return std::exchange(m_spareBuffer, { });
 }
 
 void* GraphicsContextGLIOSurfaceSwapChain::detachClient()
@@ -60,6 +59,8 @@
 
 void GraphicsContextGLIOSurfaceSwapChain::present(Buffer&& buffer)
 {
+    ASSERT(!m_spareBuffer.surface);
+    ASSERT(!m_spareBuffer.handle);
     m_spareBuffer = std::exchange(m_displayBuffer, WTFMove(buffer));
     if (m_displayBufferInUse) {
         m_displayBufferInUse = false;

Modified: trunk/Tools/ChangeLog (286159 => 286160)


--- trunk/Tools/ChangeLog	2021-11-25 06:59:55 UTC (rev 286159)
+++ trunk/Tools/ChangeLog	2021-11-25 09:17:45 UTC (rev 286160)
@@ -1,3 +1,29 @@
+2021-11-25  Kimmo Kinnunen  <[email protected]>
+
+        ANGLE Metal: The memory backing IOSurfaces of former client buffer pbuffers is leaked
+        https://bugs.webkit.org/show_bug.cgi?id=233328
+        <rdar://problem/85563187>
+
+        Reviewed by Antti Koivisto.
+
+        Add tests testing Cocoa GraphicsContextGLOpenGL drawing buffer
+        recycling behavior.
+
+        * TestWebKitAPI/Configurations/TestWebKitAPI.xcconfig:
+        * TestWebKitAPI/Sources.txt:
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebCore/ImageBufferTests.cpp:
+        (TestWebKitAPI::imageBufferPixelIs):
+        (TestWebKitAPI::memoryFootprintChangedBy): Deleted.
+        * TestWebKitAPI/Tests/WebCore/cocoa/TestGraphicsContextGLOpenGLCocoa.mm:
+        (TestWebKitAPI::createDefaultTestContext):
+        (TestWebKitAPI::changeContextContents):
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/TestUtilities.h: Added.
+        * TestWebKitAPI/TestUtilities.cpp: Added.
+        Add few useful functions to TestUtilities.h so that different
+        tests can use them.
+
 2021-11-23  Lauro Moura  <[email protected]>
 
         [GTK] GdkRGBA expects colors to be in the 0.0 to 1.0 range

Modified: trunk/Tools/TestWebKitAPI/Configurations/TestWebKitAPI.xcconfig (286159 => 286160)


--- trunk/Tools/TestWebKitAPI/Configurations/TestWebKitAPI.xcconfig	2021-11-25 06:59:55 UTC (rev 286159)
+++ trunk/Tools/TestWebKitAPI/Configurations/TestWebKitAPI.xcconfig	2021-11-25 09:17:45 UTC (rev 286160)
@@ -93,7 +93,7 @@
 
 OTHER_CPLUSPLUSFLAGS = $(inherited) -isystem $(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders;
 
-OTHER_LDFLAGS = $(inherited) -lgtest -force_load $(BUILT_PRODUCTS_DIR)/libTestWebKitAPI.a -framework _javascript_Core -framework WebKit -lWebCoreTestSupport -framework Metal $(WK_APPSERVERSUPPORT_LDFLAGS) $(WK_AUTHKIT_LDFLAGS) -framework Network $(WK_HID_LDFLAGS) $(WK_OPENGL_LDFLAGS) $(WK_PDFKIT_LDFLAGS) $(WK_SYSTEM_LDFLAGS) $(WK_UIKITMACHELPER_LDFLAGS) $(WK_VISIONKITCORE_LDFLAGS) $(OTHER_LDFLAGS_PLATFORM_$(WK_COCOA_TOUCH));
+OTHER_LDFLAGS = $(inherited) -lgtest -force_load $(BUILT_PRODUCTS_DIR)/libTestWebKitAPI.a -framework _javascript_Core -framework WebKit -lWebCoreTestSupport -framework Metal -framework IOSurface $(WK_APPSERVERSUPPORT_LDFLAGS) $(WK_AUTHKIT_LDFLAGS) -framework Network $(WK_HID_LDFLAGS) $(WK_OPENGL_LDFLAGS) $(WK_PDFKIT_LDFLAGS) $(WK_SYSTEM_LDFLAGS) $(WK_UIKITMACHELPER_LDFLAGS) $(WK_VISIONKITCORE_LDFLAGS) $(OTHER_LDFLAGS_PLATFORM_$(WK_COCOA_TOUCH));
 OTHER_LDFLAGS_PLATFORM_ = -framework Cocoa -framework Carbon;
 
 // FIXME: This should not be built on iOS. Instead we should create and use a TestWebKitAPI application.

Modified: trunk/Tools/TestWebKitAPI/Sources.txt (286159 => 286160)


--- trunk/Tools/TestWebKitAPI/Sources.txt	2021-11-25 06:59:55 UTC (rev 286159)
+++ trunk/Tools/TestWebKitAPI/Sources.txt	2021-11-25 09:17:45 UTC (rev 286160)
@@ -25,4 +25,5 @@
 _javascript_Test.cpp
 PlatformUtilities.cpp
 TestsController.cpp
+TestUtilities.cpp
 

Added: trunk/Tools/TestWebKitAPI/TestUtilities.cpp (0 => 286160)


--- trunk/Tools/TestWebKitAPI/TestUtilities.cpp	                        (rev 0)
+++ trunk/Tools/TestWebKitAPI/TestUtilities.cpp	2021-11-25 09:17:45 UTC (rev 286160)
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "TestUtilities.h"
+
+#import <wtf/MemoryFootprint.h>
+
+namespace TestWebKitAPI {
+
+::testing::AssertionResult memoryFootprintChangedBy(size_t& lastFootprint, double expectedChange, double error)
+{
+    WTF::releaseFastMallocFreeMemory();
+    size_t newFootprint = memoryFootprint();
+    size_t oldFootprint = std::exchange(lastFootprint, newFootprint);
+    double change = static_cast<double>(newFootprint) - oldFootprint;
+    if (change - expectedChange > error)
+        return ::testing::AssertionFailure() << "Footprint changed by " << change << ". Expected at most " << expectedChange << "+-" << error;
+    return ::testing::AssertionSuccess();
+}
+
+}

Added: trunk/Tools/TestWebKitAPI/TestUtilities.h (0 => 286160)


--- trunk/Tools/TestWebKitAPI/TestUtilities.h	                        (rev 0)
+++ trunk/Tools/TestWebKitAPI/TestUtilities.h	2021-11-25 09:17:45 UTC (rev 286160)
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "Test.h"
+#include "WTFStringUtilities.h"
+#include <WebCore/Color.h>
+#include <wtf/text/TextStream.h>
+
+namespace TestWebKitAPI {
+
+// Caller should initialize lastFootprint with memoryFootprint() for the initial call.
+::testing::AssertionResult memoryFootprintChangedBy(size_t& lastFootprint, double expectedChange, double error);
+
+}
+
+namespace WebCore {
+
+inline std::ostream& operator<<(std::ostream& os, const WebCore::Color& value)
+{
+    TextStream s { TextStream::LineMode::SingleLine };
+    s << value;
+    return os << s.release();
+}
+
+}

Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (286159 => 286160)


--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2021-11-25 06:59:55 UTC (rev 286159)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2021-11-25 09:17:45 UTC (rev 286160)
@@ -2372,6 +2372,8 @@
 		7B7D09692519F8F90017A078 /* WebGLNoCrashOnOtherThreadAccess.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = WebGLNoCrashOnOtherThreadAccess.mm; sourceTree = "<group>"; };
 		7BA3936B271EDFCA0015911C /* TestGraphicsContextGLOpenGLCocoa.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TestGraphicsContextGLOpenGLCocoa.mm; sourceTree = "<group>"; };
 		7BA3936D271EEC530015911C /* WebCoreUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebCoreUtilities.h; sourceTree = "<group>"; };
+		7BB754AF274E39A100D00EC1 /* TestUtilities.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TestUtilities.h; sourceTree = "<group>"; };
+		7BB754B0274E39A100D00EC1 /* TestUtilities.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TestUtilities.cpp; sourceTree = "<group>"; };
 		7C1AF7931E8DCBAB002645B9 /* PrepareForMoveToWindow.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PrepareForMoveToWindow.mm; sourceTree = "<group>"; };
 		7C3965051CDD74F90094DBB8 /* ColorTests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ColorTests.cpp; sourceTree = "<group>"; };
 		7C3DB8E21D12129B00AE8CC3 /* CommandBackForward.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CommandBackForward.mm; sourceTree = "<group>"; };
@@ -3181,6 +3183,8 @@
 				BCB9E7FA112359A300A137E0 /* Test.h */,
 				BC131AA8117131FC00B69727 /* TestsController.cpp */,
 				BCB9E7C711234E3A00A137E0 /* TestsController.h */,
+				7BB754B0274E39A100D00EC1 /* TestUtilities.cpp */,
+				7BB754AF274E39A100D00EC1 /* TestUtilities.h */,
 				7C83E0361D0A5F7000FEBCF3 /* Utilities.h */,
 				7BA3936D271EEC530015911C /* WebCoreUtilities.h */,
 				7CBD5A2222DE42A6004A9E32 /* WTFStringUtilities.cpp */,

Modified: trunk/Tools/TestWebKitAPI/Tests/WebCore/ImageBufferTests.cpp (286159 => 286160)


--- trunk/Tools/TestWebKitAPI/Tests/WebCore/ImageBufferTests.cpp	2021-11-25 06:59:55 UTC (rev 286159)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/ImageBufferTests.cpp	2021-11-25 09:17:45 UTC (rev 286160)
@@ -25,6 +25,7 @@
 
 #include "config.h"
 
+#include "TestUtilities.h"
 #include <WebCore/Color.h>
 #include <WebCore/ImageBuffer.h>
 #include <WebCore/PlatformImageBuffer.h>
@@ -34,17 +35,6 @@
 namespace TestWebKitAPI {
 using namespace WebCore;
 
-static ::testing::AssertionResult memoryFootprintChangedBy(size_t& lastFootprint, double expectedChange, double error)
-{
-    WTF::releaseFastMallocFreeMemory();
-    size_t newFootprint = memoryFootprint();
-    size_t oldFootprint = std::exchange(lastFootprint, newFootprint);
-    double change = static_cast<double>(newFootprint) - oldFootprint;
-    if (change - expectedChange > error)
-        return ::testing::AssertionFailure() << "Footprint changed by " << change << ". Expected at most " << expectedChange << "+-" << error;
-    return ::testing::AssertionSuccess();
-}
-
 static ::testing::AssertionResult imageBufferPixelIs(Color expected, ImageBuffer& imageBuffer, int x, int y)
 {
     PixelBufferFormat format { AlphaPremultiplication::Unpremultiplied, PixelFormat::RGBA8, DestinationColorSpace::SRGB() };
@@ -52,7 +42,7 @@
     auto& data = ""
     auto got = Color { SRGBA<uint8_t> { data.item(0), data.item(1), data.item(2), data.item(3) } };
     if (got != expected)
-        return ::testing::AssertionFailure() << "color is not expected."; // FIXME: implement Color <<.
+        return ::testing::AssertionFailure() << "color is not expected. Got: " << got << ", expected: " << expected << ".";
     return ::testing::AssertionSuccess();
 }
 

Modified: trunk/Tools/TestWebKitAPI/Tests/WebCore/cocoa/TestGraphicsContextGLOpenGLCocoa.mm (286159 => 286160)


--- trunk/Tools/TestWebKitAPI/Tests/WebCore/cocoa/TestGraphicsContextGLOpenGLCocoa.mm	2021-11-25 06:59:55 UTC (rev 286159)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/cocoa/TestGraphicsContextGLOpenGLCocoa.mm	2021-11-25 09:17:45 UTC (rev 286160)
@@ -27,29 +27,94 @@
 #import "Test.h"
 
 #if PLATFORM(COCOA) && ENABLE(WEBGL)
+#import "TestUtilities.h"
 #import "WebCoreUtilities.h"
 #import <Metal/Metal.h>
+#import <WebCore/Color.h>
 #import <WebCore/GraphicsContextGLOpenGL.h>
+#import <optional>
+#import <wtf/HashSet.h>
+#import <wtf/MemoryFootprint.h>
 
 namespace TestWebKitAPI {
-using namespace WebCore;
 
 namespace {
-class TestedGraphicsContextGLOpenGL : public GraphicsContextGLOpenGL {
+
+class TestedGraphicsContextGLOpenGL : public WebCore::GraphicsContextGLOpenGL {
 public:
-    static RefPtr<TestedGraphicsContextGLOpenGL> create(GraphicsContextGLAttributes attributes)
+    static RefPtr<TestedGraphicsContextGLOpenGL> create(WebCore::GraphicsContextGLAttributes attributes)
     {
         auto context = adoptRef(*new TestedGraphicsContextGLOpenGL(WTFMove(attributes)));
         return context;
     }
+    WebCore::IOSurface* displayBuffer()
+    {
+        return m_swapChain.displayBuffer().surface.get();
+    }
+    void markDisplayBufferInUse()
+    {
+        m_swapChain.markDisplayBufferInUse();
+    }
 private:
-    TestedGraphicsContextGLOpenGL(GraphicsContextGLAttributes attributes)
-        : GraphicsContextGLOpenGL(WTFMove(attributes))
+    TestedGraphicsContextGLOpenGL(WebCore::GraphicsContextGLAttributes attributes)
+        : WebCore::GraphicsContextGLOpenGL(WTFMove(attributes))
     {
     }
 };
+
+class GraphicsContextGLOpenGLCocoaTest : public ::testing::Test {
+public:
+    void SetUp() override // NOLINT
+    {
+        m_scopedProcessType = ScopedSetAuxiliaryProcessTypeForTesting { WebCore::AuxiliaryProcessType::GPU };
+    }
+    void TearDown() override // NOLINT
+    {
+        m_scopedProcessType = std::nullopt;
+    }
+private:
+    std::optional<ScopedSetAuxiliaryProcessTypeForTesting> m_scopedProcessType;
+};
+
 }
 
+static const int expectedDisplayBufferPoolSize = 3;
+
+static RefPtr<TestedGraphicsContextGLOpenGL> createDefaultTestContext(WebCore::IntSize contextSize)
+{
+    WebCore::GraphicsContextGLAttributes attributes;
+    attributes.useMetal = true;
+    attributes.antialias = false;
+    attributes.depth = false;
+    attributes.stencil = false;
+    attributes.alpha = true;
+    attributes.preserveDrawingBuffer = false;
+    auto context = TestedGraphicsContextGLOpenGL::create(attributes);
+    if (!context)
+        return nullptr;
+    context->reshape(contextSize.width(), contextSize.height());
+    return context;
+}
+
+static ::testing::AssertionResult changeContextContents(TestedGraphicsContextGLOpenGL& context, int iteration)
+{
+    context.markContextChanged();
+    WebCore::Color expected { iteration % 2 ? WebCore::Color::green : WebCore::Color::yellow };
+    auto [colorSpace, components] = expected.colorSpaceAndComponents();
+    UNUSED_VARIABLE(colorSpace);
+    context.clearColor(components[0], components[1], components[2], components[3]);
+    context.clear(WebCore::GraphicsContextGL::COLOR_BUFFER_BIT);
+    uint8_t gotValues[4] = { };
+    auto sampleAt = context.getInternalFramebufferSize();
+    sampleAt.contract(2, 3);
+    sampleAt.clampNegativeToZero();
+    context.readnPixels(sampleAt.width(), sampleAt.height(), 1, 1, WebCore::GraphicsContextGL::RGBA, WebCore::GraphicsContextGL::UNSIGNED_BYTE, gotValues);
+    WebCore::Color got { WebCore::SRGBA<uint8_t> { gotValues[0], gotValues[1], gotValues[2], gotValues[3] } };
+    if (got != expected)
+        return ::testing::AssertionFailure() << "Failed to verify draw to context. Got: " << got << ", expected: " << expected << ".";
+    return ::testing::AssertionSuccess();
+}
+
 static bool hasMultipleGPUs()
 {
 #if (PLATFORM(MAC) || PLATFORM(MACCATALYST))
@@ -68,29 +133,121 @@
 // Tests for a bug where high-performance context would use low-power GPU if low-power or default
 // context was created first. Test is applicable only for Metal, since GPU selection for OpenGL is
 // very different.
-TEST(GraphicsContextGLOpenGLCocoaTest, MAYBE_MultipleGPUsDifferentPowerPreferenceMetal)
+TEST_F(GraphicsContextGLOpenGLCocoaTest, MAYBE_MultipleGPUsDifferentPowerPreferenceMetal)
 {
     if (!hasMultipleGPUs())
         return;
-    ScopedSetAuxiliaryProcessTypeForTesting scopedProcessType { AuxiliaryProcessType::GPU };
 
-    GraphicsContextGLAttributes attributes;
+    WebCore::GraphicsContextGLAttributes attributes;
     attributes.useMetal = true;
-    EXPECT_EQ(attributes.powerPreference, GraphicsContextGLPowerPreference::Default);
+    EXPECT_EQ(attributes.powerPreference, WebCore::GraphicsContextGLPowerPreference::Default);
     auto defaultContext = TestedGraphicsContextGLOpenGL::create(attributes);
-    EXPECT_NE(defaultContext, nullptr);
+    ASSERT_NE(defaultContext, nullptr);
 
-    attributes.powerPreference = GraphicsContextGLPowerPreference::LowPower;
+    attributes.powerPreference = WebCore::GraphicsContextGLPowerPreference::LowPower;
     auto lowPowerContext = TestedGraphicsContextGLOpenGL::create(attributes);
-    EXPECT_NE(lowPowerContext, nullptr);
+    ASSERT_NE(lowPowerContext, nullptr);
 
-    attributes.powerPreference = GraphicsContextGLPowerPreference::HighPerformance;
+    attributes.powerPreference = WebCore::GraphicsContextGLPowerPreference::HighPerformance;
     auto highPerformanceContext = TestedGraphicsContextGLOpenGL::create(attributes);
-    EXPECT_NE(highPerformanceContext, nullptr);
+    ASSERT_NE(highPerformanceContext, nullptr);
 
-    EXPECT_NE(lowPowerContext->getString(GraphicsContextGL::RENDERER), highPerformanceContext->getString(GraphicsContextGL::RENDERER));
-    EXPECT_EQ(defaultContext->getString(GraphicsContextGL::RENDERER), lowPowerContext->getString(GraphicsContextGL::RENDERER));
+    EXPECT_NE(lowPowerContext->getString(WebCore::GraphicsContextGL::RENDERER), highPerformanceContext->getString(WebCore::GraphicsContextGL::RENDERER));
+    EXPECT_EQ(defaultContext->getString(WebCore::GraphicsContextGL::RENDERER), lowPowerContext->getString(WebCore::GraphicsContextGL::RENDERER));
 }
 
+TEST_F(GraphicsContextGLOpenGLCocoaTest, DisplayBuffersAreRecycled)
+{
+    auto context = createDefaultTestContext({ 20, 20 });
+    ASSERT_NE(context, nullptr);
+    RetainPtr<IOSurfaceRef> expectedDisplayBuffers[expectedDisplayBufferPoolSize];
+    for (int i = 0; i < 50; ++i) {
+        EXPECT_TRUE(changeContextContents(*context, i));
+        context->prepareForDisplay();
+        auto* surface = context->displayBuffer();
+        ASSERT_NE(surface, nullptr);
+        int slot = i % expectedDisplayBufferPoolSize;
+        if (!expectedDisplayBuffers[slot])
+            expectedDisplayBuffers[slot] = surface->surface();
+        EXPECT_EQ(expectedDisplayBuffers[slot].get(), surface->surface()) << "for i:" << i << " slot: " << slot;
+    }
+    for (int i = 0; i < expectedDisplayBufferPoolSize - 1; ++i) {
+        for (int j = i + 1; j < expectedDisplayBufferPoolSize; ++j)
+            EXPECT_NE(expectedDisplayBuffers[i].get(), expectedDisplayBuffers[j].get()) << "for i: " << i << " j:" << j;
+    }
 }
+
+// Test that drawing buffers are not recycled if `GraphicsContextGLOpenGL::markDisplayBufferInUse()`
+// is called.
+TEST_F(GraphicsContextGLOpenGLCocoaTest, DisplayBuffersAreNotRecycledWhenMarkedInUse)
+{
+    auto context = createDefaultTestContext({ 20, 20 });
+    ASSERT_NE(context, nullptr);
+    HashSet<RetainPtr<IOSurfaceRef>> seenSurfaceRefs;
+    for (int i = 0; i < 50; ++i) {
+        EXPECT_TRUE(changeContextContents(*context, i));
+        context->prepareForDisplay();
+        WebCore::IOSurface* surface = context->displayBuffer();
+        ASSERT_NE(surface, nullptr);
+        IOSurfaceRef surfaceRef = surface->surface();
+        EXPECT_NE(surfaceRef, nullptr);
+        EXPECT_FALSE(seenSurfaceRefs.contains(surfaceRef));
+        seenSurfaceRefs.add(surfaceRef);
+
+        context->markDisplayBufferInUse();
+    }
+    ASSERT_EQ(seenSurfaceRefs.size(), 50u);
+}
+
+// Test that drawing buffers are not recycled if the use count of the underlying IOSurface
+// changes. Use count is modified for example by CoreAnimation when the IOSurface is attached
+// to the contents.
+TEST_F(GraphicsContextGLOpenGLCocoaTest, DisplayBuffersAreNotRecycledWhedInUse)
+{
+    auto context = createDefaultTestContext({ 20, 20 });
+    ASSERT_NE(context, nullptr);
+    HashSet<RetainPtr<IOSurfaceRef>> seenSurfaceRefs;
+    for (int i = 0; i < 50; ++i) {
+        EXPECT_TRUE(changeContextContents(*context, i));
+        context->prepareForDisplay();
+        WebCore::IOSurface* surface = context->displayBuffer();
+        ASSERT_NE(surface, nullptr);
+        IOSurfaceRef surfaceRef = surface->surface();
+        EXPECT_NE(surfaceRef, nullptr);
+        EXPECT_FALSE(seenSurfaceRefs.contains(surfaceRef));
+        seenSurfaceRefs.add(surfaceRef);
+
+        IOSurfaceIncrementUseCount(surfaceRef);
+    }
+    ASSERT_EQ(seenSurfaceRefs.size(), 50u);
+}
+
+// Test that drawing to GraphicsContextGL and marking the display buffer in use does not leak big
+// amounts of memory for each displayed buffer.
+TEST_F(GraphicsContextGLOpenGLCocoaTest, UnrecycledDisplayBuffersNoLeaks)
+{
+    // The test detects the leak by observing memory footprint. However, some of the freed IOSurface
+    // memory (130mb) stays resident, presumably by intention of IOKit. The test would originally leak
+    // 2.7gb so the intended bug would be detected with 150mb error range.
+    size_t footprintError = 150 * 1024 * 1024;
+    size_t footprintChange = 0;
+
+    auto context = createDefaultTestContext({ 2048, 2048 });
+    ASSERT_NE(context, nullptr);
+
+    WTF::releaseFastMallocFreeMemory();
+    auto lastFootprint = memoryFootprint();
+
+    for (int i = 0; i < 50; ++i) {
+        EXPECT_TRUE(changeContextContents(*context, i));
+        context->prepareForDisplay();
+        EXPECT_NE(context->displayBuffer(), nullptr);
+        context->markDisplayBufferInUse();
+    }
+
+    EXPECT_TRUE(memoryFootprintChangedBy(lastFootprint, footprintChange, footprintError));
+}
+
+}
+
 #endif
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to