Changeset 286160 in webkit
- Timestamp:
- Nov 25, 2021, 1:17:45 AM (5 years ago)
- Location:
- trunk
- Files:
-
- 2 added
- 10 edited
-
Source/WebCore/ChangeLog (modified) (1 diff)
-
Source/WebCore/PAL/ChangeLog (modified) (1 diff)
-
Source/WebCore/PAL/pal/spi/cocoa/IOSurfaceSPI.h (modified) (1 diff)
-
Source/WebCore/platform/graphics/cocoa/GraphicsContextGLIOSurfaceSwapChain.cpp (modified) (2 diffs)
-
Tools/ChangeLog (modified) (1 diff)
-
Tools/TestWebKitAPI/Configurations/TestWebKitAPI.xcconfig (modified) (1 diff)
-
Tools/TestWebKitAPI/Sources.txt (modified) (1 diff)
-
Tools/TestWebKitAPI/TestUtilities.cpp (added)
-
Tools/TestWebKitAPI/TestUtilities.h (added)
-
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (modified) (2 diffs)
-
Tools/TestWebKitAPI/Tests/WebCore/ImageBufferTests.cpp (modified) (3 diffs)
-
Tools/TestWebKitAPI/Tests/WebCore/cocoa/TestGraphicsContextGLOpenGLCocoa.mm (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r286156 r286160 1 2021-11-25 Kimmo Kinnunen <kkinnunen@apple.com> 2 3 ANGLE Metal: The memory backing IOSurfaces of former client buffer pbuffers is leaked 4 https://bugs.webkit.org/show_bug.cgi?id=233328 5 <rdar://problem/85563187> 6 7 Reviewed by Antti Koivisto. 8 9 Fix a bug where recycling GraphicsContextGLOpenGL display buffers would 10 leak the spare buffer pbuffer handle. This would happen if the 11 display buffer was marked as "in use", so that the IOSurface reference 12 would be dropped immediately in order to not use it as next drawing buffer. 13 However, the IOSurface is still bound in ANGLE and as such, the pbuffer handle 14 must be returned during `GraphicsContextGLIOSurfaceSwapChain::recycleBuffer`` 15 call. 16 17 Adds API tests for testing the leak. 18 19 * platform/graphics/cocoa/GraphicsContextGLIOSurfaceSwapChain.cpp: 20 (WebCore::GraphicsContextGLIOSurfaceSwapChain::recycleBuffer): 21 (WebCore::GraphicsContextGLIOSurfaceSwapChain::present): 22 1 23 2021-11-24 David Kilzer <ddkilzer@apple.com> 2 24 -
trunk/Source/WebCore/PAL/ChangeLog
r286124 r286160 1 2021-11-25 Kimmo Kinnunen <kkinnunen@apple.com> 2 3 ANGLE Metal: The memory backing IOSurfaces of former client buffer pbuffers is leaked 4 https://bugs.webkit.org/show_bug.cgi?id=233328 5 <rdar://problem/85563187> 6 7 Reviewed by Antti Koivisto. 8 9 Add prototype for IOSurfaceIncrementUseCount. 10 Currently used for a test, in simulating CA behavior. 11 12 * pal/spi/cocoa/IOSurfaceSPI.h: 13 1 14 2021-11-22 Myles C. Maxfield <mmaxfield@apple.com> 2 15 -
trunk/Source/WebCore/PAL/pal/spi/cocoa/IOSurfaceSPI.h
r283483 r286160 72 72 size_t IOSurfaceGetWidth(IOSurfaceRef buffer); 73 73 OSType IOSurfaceGetPixelFormat(IOSurfaceRef buffer); 74 void IOSurfaceIncrementUseCount(IOSurfaceRef buffer); 74 75 Boolean IOSurfaceIsInUse(IOSurfaceRef buffer); 75 76 IOReturn IOSurfaceLock(IOSurfaceRef buffer, uint32_t options, uint32_t *seed); -
trunk/Source/WebCore/platform/graphics/cocoa/GraphicsContextGLIOSurfaceSwapChain.cpp
r283238 r286160 48 48 if (m_spareBuffer.surface->isInUse()) 49 49 m_spareBuffer.surface.reset(); 50 return WTFMove(m_spareBuffer);51 50 } 52 return { };51 return std::exchange(m_spareBuffer, { }); 53 52 } 54 53 … … 61 60 void GraphicsContextGLIOSurfaceSwapChain::present(Buffer&& buffer) 62 61 { 62 ASSERT(!m_spareBuffer.surface); 63 ASSERT(!m_spareBuffer.handle); 63 64 m_spareBuffer = std::exchange(m_displayBuffer, WTFMove(buffer)); 64 65 if (m_displayBufferInUse) { -
trunk/Tools/ChangeLog
r286141 r286160 1 2021-11-25 Kimmo Kinnunen <kkinnunen@apple.com> 2 3 ANGLE Metal: The memory backing IOSurfaces of former client buffer pbuffers is leaked 4 https://bugs.webkit.org/show_bug.cgi?id=233328 5 <rdar://problem/85563187> 6 7 Reviewed by Antti Koivisto. 8 9 Add tests testing Cocoa GraphicsContextGLOpenGL drawing buffer 10 recycling behavior. 11 12 * TestWebKitAPI/Configurations/TestWebKitAPI.xcconfig: 13 * TestWebKitAPI/Sources.txt: 14 * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj: 15 * TestWebKitAPI/Tests/WebCore/ImageBufferTests.cpp: 16 (TestWebKitAPI::imageBufferPixelIs): 17 (TestWebKitAPI::memoryFootprintChangedBy): Deleted. 18 * TestWebKitAPI/Tests/WebCore/cocoa/TestGraphicsContextGLOpenGLCocoa.mm: 19 (TestWebKitAPI::createDefaultTestContext): 20 (TestWebKitAPI::changeContextContents): 21 (TestWebKitAPI::TEST): 22 * TestWebKitAPI/TestUtilities.h: Added. 23 * TestWebKitAPI/TestUtilities.cpp: Added. 24 Add few useful functions to TestUtilities.h so that different 25 tests can use them. 26 1 27 2021-11-23 Lauro Moura <lmoura@igalia.com> 2 28 -
trunk/Tools/TestWebKitAPI/Configurations/TestWebKitAPI.xcconfig
r285799 r286160 94 94 OTHER_CPLUSPLUSFLAGS = $(inherited) -isystem $(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders; 95 95 96 OTHER_LDFLAGS = $(inherited) -lgtest -force_load $(BUILT_PRODUCTS_DIR)/libTestWebKitAPI.a -framework JavaScriptCore -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));96 OTHER_LDFLAGS = $(inherited) -lgtest -force_load $(BUILT_PRODUCTS_DIR)/libTestWebKitAPI.a -framework JavaScriptCore -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)); 97 97 OTHER_LDFLAGS_PLATFORM_ = -framework Cocoa -framework Carbon; 98 98 -
trunk/Tools/TestWebKitAPI/Sources.txt
r285547 r286160 26 26 PlatformUtilities.cpp 27 27 TestsController.cpp 28 TestUtilities.cpp 28 29 -
trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
r286106 r286160 2373 2373 7BA3936B271EDFCA0015911C /* TestGraphicsContextGLOpenGLCocoa.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TestGraphicsContextGLOpenGLCocoa.mm; sourceTree = "<group>"; }; 2374 2374 7BA3936D271EEC530015911C /* WebCoreUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebCoreUtilities.h; sourceTree = "<group>"; }; 2375 7BB754AF274E39A100D00EC1 /* TestUtilities.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TestUtilities.h; sourceTree = "<group>"; }; 2376 7BB754B0274E39A100D00EC1 /* TestUtilities.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TestUtilities.cpp; sourceTree = "<group>"; }; 2375 2377 7C1AF7931E8DCBAB002645B9 /* PrepareForMoveToWindow.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PrepareForMoveToWindow.mm; sourceTree = "<group>"; }; 2376 2378 7C3965051CDD74F90094DBB8 /* ColorTests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ColorTests.cpp; sourceTree = "<group>"; }; … … 3182 3184 BC131AA8117131FC00B69727 /* TestsController.cpp */, 3183 3185 BCB9E7C711234E3A00A137E0 /* TestsController.h */, 3186 7BB754B0274E39A100D00EC1 /* TestUtilities.cpp */, 3187 7BB754AF274E39A100D00EC1 /* TestUtilities.h */, 3184 3188 7C83E0361D0A5F7000FEBCF3 /* Utilities.h */, 3185 3189 7BA3936D271EEC530015911C /* WebCoreUtilities.h */, -
trunk/Tools/TestWebKitAPI/Tests/WebCore/ImageBufferTests.cpp
r285644 r286160 26 26 #include "config.h" 27 27 28 #include "TestUtilities.h" 28 29 #include <WebCore/Color.h> 29 30 #include <WebCore/ImageBuffer.h> … … 35 36 using namespace WebCore; 36 37 37 static ::testing::AssertionResult memoryFootprintChangedBy(size_t& lastFootprint, double expectedChange, double error)38 {39 WTF::releaseFastMallocFreeMemory();40 size_t newFootprint = memoryFootprint();41 size_t oldFootprint = std::exchange(lastFootprint, newFootprint);42 double change = static_cast<double>(newFootprint) - oldFootprint;43 if (change - expectedChange > error)44 return ::testing::AssertionFailure() << "Footprint changed by " << change << ". Expected at most " << expectedChange << "+-" << error;45 return ::testing::AssertionSuccess();46 }47 48 38 static ::testing::AssertionResult imageBufferPixelIs(Color expected, ImageBuffer& imageBuffer, int x, int y) 49 39 { … … 53 43 auto got = Color { SRGBA<uint8_t> { data.item(0), data.item(1), data.item(2), data.item(3) } }; 54 44 if (got != expected) 55 return ::testing::AssertionFailure() << "color is not expected. "; // FIXME: implement Color <<.45 return ::testing::AssertionFailure() << "color is not expected. Got: " << got << ", expected: " << expected << "."; 56 46 return ::testing::AssertionSuccess(); 57 47 } -
trunk/Tools/TestWebKitAPI/Tests/WebCore/cocoa/TestGraphicsContextGLOpenGLCocoa.mm
r284669 r286160 28 28 29 29 #if PLATFORM(COCOA) && ENABLE(WEBGL) 30 #import "TestUtilities.h" 30 31 #import "WebCoreUtilities.h" 31 32 #import <Metal/Metal.h> 33 #import <WebCore/Color.h> 32 34 #import <WebCore/GraphicsContextGLOpenGL.h> 35 #import <optional> 36 #import <wtf/HashSet.h> 37 #import <wtf/MemoryFootprint.h> 33 38 34 39 namespace TestWebKitAPI { 35 using namespace WebCore;36 40 37 41 namespace { 38 class TestedGraphicsContextGLOpenGL : public GraphicsContextGLOpenGL { 42 43 class TestedGraphicsContextGLOpenGL : public WebCore::GraphicsContextGLOpenGL { 39 44 public: 40 static RefPtr<TestedGraphicsContextGLOpenGL> create( GraphicsContextGLAttributes attributes)45 static RefPtr<TestedGraphicsContextGLOpenGL> create(WebCore::GraphicsContextGLAttributes attributes) 41 46 { 42 47 auto context = adoptRef(*new TestedGraphicsContextGLOpenGL(WTFMove(attributes))); 43 48 return context; 44 49 } 50 WebCore::IOSurface* displayBuffer() 51 { 52 return m_swapChain.displayBuffer().surface.get(); 53 } 54 void markDisplayBufferInUse() 55 { 56 m_swapChain.markDisplayBufferInUse(); 57 } 45 58 private: 46 TestedGraphicsContextGLOpenGL( GraphicsContextGLAttributes attributes)47 : GraphicsContextGLOpenGL(WTFMove(attributes))59 TestedGraphicsContextGLOpenGL(WebCore::GraphicsContextGLAttributes attributes) 60 : WebCore::GraphicsContextGLOpenGL(WTFMove(attributes)) 48 61 { 49 62 } 50 63 }; 64 65 class GraphicsContextGLOpenGLCocoaTest : public ::testing::Test { 66 public: 67 void SetUp() override // NOLINT 68 { 69 m_scopedProcessType = ScopedSetAuxiliaryProcessTypeForTesting { WebCore::AuxiliaryProcessType::GPU }; 70 } 71 void TearDown() override // NOLINT 72 { 73 m_scopedProcessType = std::nullopt; 74 } 75 private: 76 std::optional<ScopedSetAuxiliaryProcessTypeForTesting> m_scopedProcessType; 77 }; 78 79 } 80 81 static const int expectedDisplayBufferPoolSize = 3; 82 83 static RefPtr<TestedGraphicsContextGLOpenGL> createDefaultTestContext(WebCore::IntSize contextSize) 84 { 85 WebCore::GraphicsContextGLAttributes attributes; 86 attributes.useMetal = true; 87 attributes.antialias = false; 88 attributes.depth = false; 89 attributes.stencil = false; 90 attributes.alpha = true; 91 attributes.preserveDrawingBuffer = false; 92 auto context = TestedGraphicsContextGLOpenGL::create(attributes); 93 if (!context) 94 return nullptr; 95 context->reshape(contextSize.width(), contextSize.height()); 96 return context; 97 } 98 99 static ::testing::AssertionResult changeContextContents(TestedGraphicsContextGLOpenGL& context, int iteration) 100 { 101 context.markContextChanged(); 102 WebCore::Color expected { iteration % 2 ? WebCore::Color::green : WebCore::Color::yellow }; 103 auto [colorSpace, components] = expected.colorSpaceAndComponents(); 104 UNUSED_VARIABLE(colorSpace); 105 context.clearColor(components[0], components[1], components[2], components[3]); 106 context.clear(WebCore::GraphicsContextGL::COLOR_BUFFER_BIT); 107 uint8_t gotValues[4] = { }; 108 auto sampleAt = context.getInternalFramebufferSize(); 109 sampleAt.contract(2, 3); 110 sampleAt.clampNegativeToZero(); 111 context.readnPixels(sampleAt.width(), sampleAt.height(), 1, 1, WebCore::GraphicsContextGL::RGBA, WebCore::GraphicsContextGL::UNSIGNED_BYTE, gotValues); 112 WebCore::Color got { WebCore::SRGBA<uint8_t> { gotValues[0], gotValues[1], gotValues[2], gotValues[3] } }; 113 if (got != expected) 114 return ::testing::AssertionFailure() << "Failed to verify draw to context. Got: " << got << ", expected: " << expected << "."; 115 return ::testing::AssertionSuccess(); 51 116 } 52 117 … … 69 134 // context was created first. Test is applicable only for Metal, since GPU selection for OpenGL is 70 135 // very different. 71 TEST (GraphicsContextGLOpenGLCocoaTest, MAYBE_MultipleGPUsDifferentPowerPreferenceMetal)136 TEST_F(GraphicsContextGLOpenGLCocoaTest, MAYBE_MultipleGPUsDifferentPowerPreferenceMetal) 72 137 { 73 138 if (!hasMultipleGPUs()) 74 139 return; 75 ScopedSetAuxiliaryProcessTypeForTesting scopedProcessType { AuxiliaryProcessType::GPU }; 76 77 GraphicsContextGLAttributes attributes; 140 141 WebCore::GraphicsContextGLAttributes attributes; 78 142 attributes.useMetal = true; 79 EXPECT_EQ(attributes.powerPreference, GraphicsContextGLPowerPreference::Default);143 EXPECT_EQ(attributes.powerPreference, WebCore::GraphicsContextGLPowerPreference::Default); 80 144 auto defaultContext = TestedGraphicsContextGLOpenGL::create(attributes); 81 EXPECT_NE(defaultContext, nullptr);82 83 attributes.powerPreference = GraphicsContextGLPowerPreference::LowPower;145 ASSERT_NE(defaultContext, nullptr); 146 147 attributes.powerPreference = WebCore::GraphicsContextGLPowerPreference::LowPower; 84 148 auto lowPowerContext = TestedGraphicsContextGLOpenGL::create(attributes); 85 EXPECT_NE(lowPowerContext, nullptr);86 87 attributes.powerPreference = GraphicsContextGLPowerPreference::HighPerformance;149 ASSERT_NE(lowPowerContext, nullptr); 150 151 attributes.powerPreference = WebCore::GraphicsContextGLPowerPreference::HighPerformance; 88 152 auto highPerformanceContext = TestedGraphicsContextGLOpenGL::create(attributes); 89 EXPECT_NE(highPerformanceContext, nullptr); 90 91 EXPECT_NE(lowPowerContext->getString(GraphicsContextGL::RENDERER), highPerformanceContext->getString(GraphicsContextGL::RENDERER)); 92 EXPECT_EQ(defaultContext->getString(GraphicsContextGL::RENDERER), lowPowerContext->getString(GraphicsContextGL::RENDERER)); 93 } 94 95 } 153 ASSERT_NE(highPerformanceContext, nullptr); 154 155 EXPECT_NE(lowPowerContext->getString(WebCore::GraphicsContextGL::RENDERER), highPerformanceContext->getString(WebCore::GraphicsContextGL::RENDERER)); 156 EXPECT_EQ(defaultContext->getString(WebCore::GraphicsContextGL::RENDERER), lowPowerContext->getString(WebCore::GraphicsContextGL::RENDERER)); 157 } 158 159 TEST_F(GraphicsContextGLOpenGLCocoaTest, DisplayBuffersAreRecycled) 160 { 161 auto context = createDefaultTestContext({ 20, 20 }); 162 ASSERT_NE(context, nullptr); 163 RetainPtr<IOSurfaceRef> expectedDisplayBuffers[expectedDisplayBufferPoolSize]; 164 for (int i = 0; i < 50; ++i) { 165 EXPECT_TRUE(changeContextContents(*context, i)); 166 context->prepareForDisplay(); 167 auto* surface = context->displayBuffer(); 168 ASSERT_NE(surface, nullptr); 169 int slot = i % expectedDisplayBufferPoolSize; 170 if (!expectedDisplayBuffers[slot]) 171 expectedDisplayBuffers[slot] = surface->surface(); 172 EXPECT_EQ(expectedDisplayBuffers[slot].get(), surface->surface()) << "for i:" << i << " slot: " << slot; 173 } 174 for (int i = 0; i < expectedDisplayBufferPoolSize - 1; ++i) { 175 for (int j = i + 1; j < expectedDisplayBufferPoolSize; ++j) 176 EXPECT_NE(expectedDisplayBuffers[i].get(), expectedDisplayBuffers[j].get()) << "for i: " << i << " j:" << j; 177 } 178 } 179 180 // Test that drawing buffers are not recycled if `GraphicsContextGLOpenGL::markDisplayBufferInUse()` 181 // is called. 182 TEST_F(GraphicsContextGLOpenGLCocoaTest, DisplayBuffersAreNotRecycledWhenMarkedInUse) 183 { 184 auto context = createDefaultTestContext({ 20, 20 }); 185 ASSERT_NE(context, nullptr); 186 HashSet<RetainPtr<IOSurfaceRef>> seenSurfaceRefs; 187 for (int i = 0; i < 50; ++i) { 188 EXPECT_TRUE(changeContextContents(*context, i)); 189 context->prepareForDisplay(); 190 WebCore::IOSurface* surface = context->displayBuffer(); 191 ASSERT_NE(surface, nullptr); 192 IOSurfaceRef surfaceRef = surface->surface(); 193 EXPECT_NE(surfaceRef, nullptr); 194 EXPECT_FALSE(seenSurfaceRefs.contains(surfaceRef)); 195 seenSurfaceRefs.add(surfaceRef); 196 197 context->markDisplayBufferInUse(); 198 } 199 ASSERT_EQ(seenSurfaceRefs.size(), 50u); 200 } 201 202 // Test that drawing buffers are not recycled if the use count of the underlying IOSurface 203 // changes. Use count is modified for example by CoreAnimation when the IOSurface is attached 204 // to the contents. 205 TEST_F(GraphicsContextGLOpenGLCocoaTest, DisplayBuffersAreNotRecycledWhedInUse) 206 { 207 auto context = createDefaultTestContext({ 20, 20 }); 208 ASSERT_NE(context, nullptr); 209 HashSet<RetainPtr<IOSurfaceRef>> seenSurfaceRefs; 210 for (int i = 0; i < 50; ++i) { 211 EXPECT_TRUE(changeContextContents(*context, i)); 212 context->prepareForDisplay(); 213 WebCore::IOSurface* surface = context->displayBuffer(); 214 ASSERT_NE(surface, nullptr); 215 IOSurfaceRef surfaceRef = surface->surface(); 216 EXPECT_NE(surfaceRef, nullptr); 217 EXPECT_FALSE(seenSurfaceRefs.contains(surfaceRef)); 218 seenSurfaceRefs.add(surfaceRef); 219 220 IOSurfaceIncrementUseCount(surfaceRef); 221 } 222 ASSERT_EQ(seenSurfaceRefs.size(), 50u); 223 } 224 225 // Test that drawing to GraphicsContextGL and marking the display buffer in use does not leak big 226 // amounts of memory for each displayed buffer. 227 TEST_F(GraphicsContextGLOpenGLCocoaTest, UnrecycledDisplayBuffersNoLeaks) 228 { 229 // The test detects the leak by observing memory footprint. However, some of the freed IOSurface 230 // memory (130mb) stays resident, presumably by intention of IOKit. The test would originally leak 231 // 2.7gb so the intended bug would be detected with 150mb error range. 232 size_t footprintError = 150 * 1024 * 1024; 233 size_t footprintChange = 0; 234 235 auto context = createDefaultTestContext({ 2048, 2048 }); 236 ASSERT_NE(context, nullptr); 237 238 WTF::releaseFastMallocFreeMemory(); 239 auto lastFootprint = memoryFootprint(); 240 241 for (int i = 0; i < 50; ++i) { 242 EXPECT_TRUE(changeContextContents(*context, i)); 243 context->prepareForDisplay(); 244 EXPECT_NE(context->displayBuffer(), nullptr); 245 context->markDisplayBufferInUse(); 246 } 247 248 EXPECT_TRUE(memoryFootprintChangedBy(lastFootprint, footprintChange, footprintError)); 249 } 250 251 } 252 96 253 #endif
Note:
See TracChangeset
for help on using the changeset viewer.