Diff
Modified: trunk/Source/WebCore/ChangeLog (126759 => 126760)
--- trunk/Source/WebCore/ChangeLog 2012-08-27 14:20:46 UTC (rev 126759)
+++ trunk/Source/WebCore/ChangeLog 2012-08-27 14:27:50 UTC (rev 126760)
@@ -1,3 +1,82 @@
+2012-08-27 Justin Novosad <[email protected]>
+
+ [Chromium] Implementing a global limit on memory consumed by deferred 2D canvases
+ https://bugs.webkit.org/show_bug.cgi?id=94386
+
+ Reviewed by Stephen White.
+
+ Before this change, there was no global bound on memory that could be
+ consumed for the purpose of recording 2d canvas deferred draw commands.
+ There was only a per canvas limit. It is possible for canvases to get
+ dereferenced without flushing pending draw commands, which may lock
+ resources until the canvas element is garbage collected. This makes
+ it possible to grow memory consumption indefinitely by hitting reload
+ successively on some canvas-intensive web pages.
+
+ The solution implemented in this change consists in maintaining a
+ global registry of deferred canvas layers and tracking the sum of
+ memory allocated by all canvases for the purpose of storing deferred
+ draw commands. When memory consumption reaches the allowed limit,
+ caches are cleared and draw operations are flushed on one or several
+ canvases, starting with the least recently used, until memory
+ consumption is below a target level.
+
+ New tests: webkit_unit_tests DeferredLayerManagerTest*
+
+ * WebCore.gypi:
+ * platform/graphics/chromium/Canvas2DLayerBridge.cpp:
+ (WebCore::Canvas2DLayerBridge::Canvas2DLayerBridge):
+ (WebCore::Canvas2DLayerBridge::~Canvas2DLayerBridge):
+ (WebCore::Canvas2DLayerBridge::storageAllocatedForRecordingChanged):
+ Overloaded from SkDeferredCanvas::NotificationClient. Called when
+ there is a change in the number of bytes allocated by the deferred
+ canvas.
+ (WebCore):
+ (WebCore::Canvas2DLayerBridge::flushedDrawCommands):
+ Overloaded from SkDeferredCanvas::NotificationClient. Called when
+ pending draw commands are flush by the deferred canvas.
+ (WebCore::Canvas2DLayerBridge::freeMemoryIfPossible):
+ Called by the Canvas2DLayerManager to request the release of unlocked
+ memory resources (caches). Request is relayed to SkDeferredCanvas.
+ returns number of bytes freed.
+ (WebCore::Canvas2DLayerBridge::flush):
+ Requests that pending draw commands be flushed immediately from the
+ deferred canvas queue. Request is relayed to SkDeferredCanvas.
+ (WebCore::Canvas2DLayerBridge::contextAcquired):
+ * platform/graphics/chromium/Canvas2DLayerBridge.h:
+ (Canvas2DLayerBridge):
+ (WebCore::Canvas2DLayerBridge::bytesAllocated):
+ (WebCore::Canvas2DLayerBridge::updateBytesAllocated):
+ * platform/graphics/chromium/Canvas2DLayerManager.cpp: Added.
+ (WebCore):
+ (WebCore::Canvas2DLayerManager::Canvas2DLayerManager):
+ (WebCore::Canvas2DLayerManager::~Canvas2DLayerManager):
+ (WebCore::Canvas2DLayerManager::init):
+ Sets the global limit for the maximum total number of bytes allocated
+ by deferred layers.
+ (WebCore::Canvas2DLayerManager::get):
+ Singleton accessor
+ (WebCore::Canvas2DLayerManager::layerDidDraw):
+ Called by Canvas2DLayerBridge to indicate that the layer was drawn to.
+ (WebCore::Canvas2DLayerManager::addLayerToList):
+ (WebCore::Canvas2DLayerManager::layerAllocatedStorageChanged):
+ Called by Canvas2DLayerBridge to indicate thet the memory consumed by
+ the layer has changed.
+ (WebCore::Canvas2DLayerManager::layerDidFlush):
+ Called by Canvas2DLayerBridge to indicated that the pending draw
+ commands of the layer were flushed.
+ (WebCore::Canvas2DLayerManager::layerToBeDestroyed):
+ Called by Canvas2DLayerBridge upon destruction.
+ (WebCore::Canvas2DLayerManager::freeMemoryIfNecessary):
+ Called internally. Check that current memory consumption is below
+ the tolerated limit. If check fails, intiates resource releases.
+ (WebCore::Canvas2DLayerManager::removeLayerFromList):
+ (WebCore::Canvas2DLayerManager::isInList):
+ (WebCore::Canvas2DLayerManager::updateBytesAllocated):
+ * platform/graphics/chromium/Canvas2DLayerManager.h: Added.
+ (WebCore):
+ (Canvas2DLayerManager):
+
2012-08-27 Zeno Albisser <[email protected]>
Implement GraphicsSurface::paintToTextureMapper.
Modified: trunk/Source/WebCore/WebCore.gypi (126759 => 126760)
--- trunk/Source/WebCore/WebCore.gypi 2012-08-27 14:20:46 UTC (rev 126759)
+++ trunk/Source/WebCore/WebCore.gypi 2012-08-27 14:27:50 UTC (rev 126760)
@@ -3653,6 +3653,8 @@
'platform/graphics/chromium/AnimationTranslationUtil.h',
'platform/graphics/chromium/Canvas2DLayerBridge.cpp',
'platform/graphics/chromium/Canvas2DLayerBridge.h',
+ 'platform/graphics/chromium/Canvas2DLayerManager.cpp',
+ 'platform/graphics/chromium/Canvas2DLayerManager.h',
'platform/graphics/chromium/CompositorHUDFontAtlas.cpp',
'platform/graphics/chromium/CompositorHUDFontAtlas.h',
'platform/graphics/chromium/CrossProcessFontLoading.h',
Modified: trunk/Source/WebCore/platform/graphics/chromium/Canvas2DLayerBridge.cpp (126759 => 126760)
--- trunk/Source/WebCore/platform/graphics/chromium/Canvas2DLayerBridge.cpp 2012-08-27 14:20:46 UTC (rev 126759)
+++ trunk/Source/WebCore/platform/graphics/chromium/Canvas2DLayerBridge.cpp 2012-08-27 14:27:50 UTC (rev 126760)
@@ -28,6 +28,7 @@
#include "Canvas2DLayerBridge.h"
#include "CCRendererGL.h" // For the GLC() macro.
+#include "Canvas2DLayerManager.h"
#include "GrContext.h"
#include "GraphicsContext3D.h"
#include "GraphicsContext3DPrivate.h"
@@ -53,6 +54,9 @@
, m_size(size)
, m_canvas(0)
, m_context(context)
+ , m_bytesAllocated(0)
+ , m_next(0)
+ , m_prev(0)
{
if (m_useDoubleBuffering) {
m_context->makeContextCurrent();
@@ -76,6 +80,7 @@
Canvas2DLayerBridge::~Canvas2DLayerBridge()
{
+ Canvas2DLayerManager::get().layerToBeDestroyed(this);
if (SkDeferredCanvas* deferred = deferredCanvas())
deferred->setNotificationClient(0);
m_layer->setTextureId(0);
@@ -101,6 +106,34 @@
m_context->makeContextCurrent();
}
+void Canvas2DLayerBridge::storageAllocatedForRecordingChanged(size_t bytesAllocated)
+{
+ ASSERT(m_deferralMode == Deferred);
+ intptr_t delta = (intptr_t)bytesAllocated - (intptr_t)m_bytesAllocated;
+ m_bytesAllocated = bytesAllocated;
+ Canvas2DLayerManager::get().layerAllocatedStorageChanged(this, delta);
+}
+
+void Canvas2DLayerBridge::flushedDrawCommands()
+{
+ storageAllocatedForRecordingChanged(deferredCanvas()->storageAllocatedForRecording());
+}
+
+size_t Canvas2DLayerBridge::freeMemoryIfPossible(size_t bytesToFree)
+{
+ ASSERT(deferredCanvas());
+ size_t bytesFreed = deferredCanvas()->freeMemoryIfPossible(bytesToFree);
+ Canvas2DLayerManager::get().layerAllocatedStorageChanged(this, -((intptr_t)bytesFreed));
+ m_bytesAllocated -= bytesFreed;
+ return bytesFreed;
+}
+
+void Canvas2DLayerBridge::flush()
+{
+ ASSERT(deferredCanvas());
+ m_canvas->flush();
+}
+
SkCanvas* Canvas2DLayerBridge::skCanvas(SkDevice* device)
{
ASSERT(!m_canvas);
@@ -154,6 +187,8 @@
{
if (m_deferralMode == NonDeferred && !m_useDoubleBuffering)
m_layer->willModifyTexture();
+ else if (m_deferralMode == Deferred)
+ Canvas2DLayerManager::get().layerDidDraw(this);
}
unsigned Canvas2DLayerBridge::backBufferTexture()
Modified: trunk/Source/WebCore/platform/graphics/chromium/Canvas2DLayerBridge.h (126759 => 126760)
--- trunk/Source/WebCore/platform/graphics/chromium/Canvas2DLayerBridge.h 2012-08-27 14:20:46 UTC (rev 126759)
+++ trunk/Source/WebCore/platform/graphics/chromium/Canvas2DLayerBridge.h 2012-08-27 14:27:50 UTC (rev 126760)
@@ -32,6 +32,7 @@
#include "SkDeferredCanvas.h"
#include <public/WebExternalTextureLayer.h>
#include <public/WebExternalTextureLayerClient.h>
+#include <wtf/DoublyLinkedList.h>
#include <wtf/PassOwnPtr.h>
#include <wtf/RefPtr.h>
@@ -43,7 +44,7 @@
class LayerChromium;
-class Canvas2DLayerBridge : public WebKit::WebExternalTextureLayerClient, public SkDeferredCanvas::NotificationClient {
+class Canvas2DLayerBridge : public WebKit::WebExternalTextureLayerClient, public SkDeferredCanvas::NotificationClient, public DoublyLinkedListNode<Canvas2DLayerBridge> {
WTF_MAKE_NONCOPYABLE(Canvas2DLayerBridge);
public:
static PassOwnPtr<Canvas2DLayerBridge> create(PassRefPtr<GraphicsContext3D> context, const IntSize& size, DeferralMode deferralMode, unsigned textureId)
@@ -58,15 +59,22 @@
virtual WebKit::WebGraphicsContext3D* context() OVERRIDE;
// SkDeferredCanvas::NotificationClient implementation
- virtual void prepareForDraw();
+ virtual void prepareForDraw() OVERRIDE;
+ virtual void storageAllocatedForRecordingChanged(size_t) OVERRIDE;
+ virtual void flushedDrawCommands() OVERRIDE;
+ // Methods used by Canvas2DLayerManager
+ virtual size_t freeMemoryIfPossible(size_t); // virtual for mocking
+ virtual void flush(); // virtual for mocking
+ size_t bytesAllocated() const {return m_bytesAllocated;}
+
SkCanvas* skCanvas(SkDevice*);
WebKit::WebLayer* layer();
void contextAcquired();
unsigned backBufferTexture();
-private:
+protected:
Canvas2DLayerBridge(PassRefPtr<GraphicsContext3D>, const IntSize&, DeferralMode, unsigned textureId);
SkDeferredCanvas* deferredCanvas();
@@ -78,6 +86,11 @@
SkCanvas* m_canvas;
OwnPtr<WebKit::WebExternalTextureLayer> m_layer;
RefPtr<GraphicsContext3D> m_context;
+ size_t m_bytesAllocated;
+
+ friend class WTF::DoublyLinkedListNode<Canvas2DLayerBridge>;
+ Canvas2DLayerBridge* m_next;
+ Canvas2DLayerBridge* m_prev;
};
}
Added: trunk/Source/WebCore/platform/graphics/chromium/Canvas2DLayerManager.cpp (0 => 126760)
--- trunk/Source/WebCore/platform/graphics/chromium/Canvas2DLayerManager.cpp (rev 0)
+++ trunk/Source/WebCore/platform/graphics/chromium/Canvas2DLayerManager.cpp 2012-08-27 14:27:50 UTC (rev 126760)
@@ -0,0 +1,136 @@
+/*
+Copyright (C) 2012 Google 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 "Canvas2DLayerManager.h"
+
+#include <wtf/StdLibExtras.h>
+
+namespace {
+enum {
+ DefaultMaxBytesAllocated = 64*1024*1024,
+ DefaultTargetBytesAllocated = 16*1024*1024,
+};
+}
+
+namespace WebCore {
+
+Canvas2DLayerManager::Canvas2DLayerManager()
+ : m_bytesAllocated(0)
+ , m_maxBytesAllocated(DefaultMaxBytesAllocated)
+ , m_targetBytesAllocated(DefaultTargetBytesAllocated)
+{
+}
+
+Canvas2DLayerManager::~Canvas2DLayerManager()
+{
+ ASSERT(!m_bytesAllocated);
+ ASSERT(!m_layerList.head());
+}
+
+void Canvas2DLayerManager::init(size_t maxBytesAllocated, size_t targetBytesAllocated)
+{
+ ASSERT(maxBytesAllocated >= targetBytesAllocated);
+ m_maxBytesAllocated = maxBytesAllocated;
+ m_targetBytesAllocated = targetBytesAllocated;
+}
+
+Canvas2DLayerManager& Canvas2DLayerManager::get()
+{
+ DEFINE_STATIC_LOCAL(Canvas2DLayerManager, manager, ());
+ return manager;
+}
+
+void Canvas2DLayerManager::layerDidDraw(Canvas2DLayerBridge* layer)
+{
+ if (isInList(layer)) {
+ if (layer != m_layerList.head()) {
+ m_layerList.remove(layer);
+ m_layerList.push(layer); // Set as MRU
+ }
+ } else
+ addLayerToList(layer);
+}
+
+void Canvas2DLayerManager::addLayerToList(Canvas2DLayerBridge* layer)
+{
+ ASSERT(!isInList(layer));
+ m_bytesAllocated += layer->bytesAllocated();
+ m_layerList.push(layer); // Set as MRU
+}
+
+void Canvas2DLayerManager::layerAllocatedStorageChanged(Canvas2DLayerBridge* layer, intptr_t deltaBytes)
+{
+ if (!isInList(layer))
+ addLayerToList(layer);
+ else {
+ ASSERT((intptr_t)m_bytesAllocated + deltaBytes >= 0);
+ m_bytesAllocated = (intptr_t)m_bytesAllocated + deltaBytes;
+ }
+ freeMemoryIfNecessary();
+}
+
+void Canvas2DLayerManager::layerToBeDestroyed(Canvas2DLayerBridge* layer)
+{
+ if (isInList(layer))
+ removeLayerFromList(layer);
+}
+
+void Canvas2DLayerManager::freeMemoryIfNecessary()
+{
+ if (m_bytesAllocated > m_maxBytesAllocated) {
+ // Pass 1: Free memory from caches
+ Canvas2DLayerBridge* layer = m_layerList.tail(); // LRU
+ while (m_bytesAllocated > m_targetBytesAllocated && layer) {
+ layer->freeMemoryIfPossible(m_bytesAllocated - m_targetBytesAllocated);
+ layer = layer->prev();
+ }
+
+ // Pass 2: Flush canvases
+ Canvas2DLayerBridge* leastRecentlyUsedLayer = m_layerList.tail();
+ while (m_bytesAllocated > m_targetBytesAllocated && leastRecentlyUsedLayer) {
+ leastRecentlyUsedLayer->flush();
+ leastRecentlyUsedLayer->freeMemoryIfPossible(~0);
+ removeLayerFromList(leastRecentlyUsedLayer);
+ leastRecentlyUsedLayer = m_layerList.tail();
+ }
+ }
+}
+
+void Canvas2DLayerManager::removeLayerFromList(Canvas2DLayerBridge* layer)
+{
+ ASSERT(isInList(layer));
+ m_bytesAllocated -= layer->bytesAllocated();
+ m_layerList.remove(layer);
+ layer->setNext(0);
+ layer->setPrev(0);
+}
+
+bool Canvas2DLayerManager::isInList(Canvas2DLayerBridge* layer)
+{
+ return layer->prev() || m_layerList.head() == layer;
+}
+
+}
+
Added: trunk/Source/WebCore/platform/graphics/chromium/Canvas2DLayerManager.h (0 => 126760)
--- trunk/Source/WebCore/platform/graphics/chromium/Canvas2DLayerManager.h (rev 0)
+++ trunk/Source/WebCore/platform/graphics/chromium/Canvas2DLayerManager.h 2012-08-27 14:27:50 UTC (rev 126760)
@@ -0,0 +1,63 @@
+/*
+Copyright (C) 2012 Google 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.
+*/
+
+#ifndef Canvas2DLayerManager_h
+#define Canvas2DLayerManager_h
+
+#include "Canvas2DLayerBridge.h"
+
+class Canvas2DLayerManagerTest;
+
+namespace WebCore {
+
+class Canvas2DLayerManager {
+public:
+ static Canvas2DLayerManager& get();
+ void init(size_t maxBytesAllocated, size_t targetBytesAllocated);
+ virtual ~Canvas2DLayerManager();
+
+ void layerAllocatedStorageChanged(Canvas2DLayerBridge*, intptr_t deltaBytes);
+ void layerDidDraw(Canvas2DLayerBridge*);
+ void layerToBeDestroyed(Canvas2DLayerBridge*);
+private:
+ Canvas2DLayerManager();
+
+ // internal methods
+ void freeMemoryIfNecessary();
+ bool isInList(Canvas2DLayerBridge*);
+ void addLayerToList(Canvas2DLayerBridge*);
+ void removeLayerFromList(Canvas2DLayerBridge*);
+
+ size_t m_bytesAllocated;
+ size_t m_maxBytesAllocated;
+ size_t m_targetBytesAllocated;
+ DoublyLinkedList<Canvas2DLayerBridge> m_layerList;
+
+ friend class ::Canvas2DLayerManagerTest; // for unit testing
+};
+
+}
+
+#endif
+
Modified: trunk/Source/WebKit/chromium/ChangeLog (126759 => 126760)
--- trunk/Source/WebKit/chromium/ChangeLog 2012-08-27 14:20:46 UTC (rev 126759)
+++ trunk/Source/WebKit/chromium/ChangeLog 2012-08-27 14:27:50 UTC (rev 126760)
@@ -1,3 +1,14 @@
+2012-08-27 Justin Novosad <[email protected]>
+
+ [Chromium] Implementing a global limit on memory consumed by deferred 2D canvases
+ https://bugs.webkit.org/show_bug.cgi?id=94386
+
+ Reviewed by Stephen White.
+
+ Adding unit tests for WebCore::Canvas2DLayerManager
+
+ * WebKit.gypi:
+
2012-08-27 Sheriff Bot <[email protected]>
Unreviewed. Rolled DEPS.
Modified: trunk/Source/WebKit/chromium/WebKit.gypi (126759 => 126760)
--- trunk/Source/WebKit/chromium/WebKit.gypi 2012-08-27 14:20:46 UTC (rev 126759)
+++ trunk/Source/WebKit/chromium/WebKit.gypi 2012-08-27 14:27:50 UTC (rev 126760)
@@ -56,6 +56,7 @@
'tests/ArenaTestHelpers.h',
'tests/AssociatedURLLoaderTest.cpp',
'tests/Canvas2DLayerBridgeTest.cpp',
+ 'tests/Canvas2DLayerManagerTest.cpp',
'tests/CCActiveAnimationTest.cpp',
'tests/CCAnimationTestCommon.cpp',
'tests/CCAnimationTestCommon.h',
Added: trunk/Source/WebKit/chromium/tests/Canvas2DLayerManagerTest.cpp (0 => 126760)
--- trunk/Source/WebKit/chromium/tests/Canvas2DLayerManagerTest.cpp (rev 0)
+++ trunk/Source/WebKit/chromium/tests/Canvas2DLayerManagerTest.cpp 2012-08-27 14:27:50 UTC (rev 126760)
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2012 Google 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 "Canvas2DLayerManager.h"
+
+#include "FakeWebGraphicsContext3D.h"
+#include "GraphicsContext3DPrivate.h"
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using namespace WebCore;
+using testing::InSequence;
+using testing::Return;
+using testing::Test;
+
+
+class FakeCanvas2DLayerBridge : public Canvas2DLayerBridge {
+public:
+ FakeCanvas2DLayerBridge()
+ : Canvas2DLayerBridge(GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(new WebKit::FakeWebGraphicsContext3D)), IntSize(1, 1), Deferred, 0)
+ , m_freeableBytes(0)
+ , m_freeMemoryIfPossibleCount(0)
+ , m_flushCount(0)
+ {
+ }
+
+ void fakeFreeableBytes(size_t size)
+ {
+ m_freeableBytes = size;
+ }
+
+ virtual size_t freeMemoryIfPossible(size_t size) OVERRIDE
+ {
+ m_freeMemoryIfPossibleCount++;
+ size_t bytesFreed = size < m_freeableBytes ? size : m_freeableBytes;
+ m_freeableBytes -= bytesFreed;
+ Canvas2DLayerManager::get().layerAllocatedStorageChanged(this, -((intptr_t)bytesFreed));
+ m_bytesAllocated -= bytesFreed;
+ return bytesFreed;
+ }
+
+ virtual void flush() OVERRIDE
+ {
+ m_flushCount++;
+ }
+
+public:
+ size_t m_freeableBytes;
+ int m_freeMemoryIfPossibleCount;
+ int m_flushCount;
+};
+
+class Canvas2DLayerManagerTest : public Test {
+protected:
+ void storageAllocationTrackingTest()
+ {
+ Canvas2DLayerManager& manager = Canvas2DLayerManager::get();
+ manager.init(10, 10);
+ {
+ FakeCanvas2DLayerBridge layer1;
+ EXPECT_EQ((size_t)0, manager.m_bytesAllocated);
+ layer1.storageAllocatedForRecordingChanged(1);
+ EXPECT_EQ((size_t)1, manager.m_bytesAllocated);
+ // Test allocation increase
+ layer1.storageAllocatedForRecordingChanged(2);
+ EXPECT_EQ((size_t)2, manager.m_bytesAllocated);
+ // Test allocation decrease
+ layer1.storageAllocatedForRecordingChanged(1);
+ EXPECT_EQ((size_t)1, manager.m_bytesAllocated);
+ {
+ FakeCanvas2DLayerBridge layer2;
+ EXPECT_EQ((size_t)1, manager.m_bytesAllocated);
+ // verify multi-layer allocation tracking
+ layer2.storageAllocatedForRecordingChanged(2);
+ EXPECT_EQ((size_t)3, manager.m_bytesAllocated);
+ }
+ // Verify tracking after destruction
+ EXPECT_EQ((size_t)1, manager.m_bytesAllocated);
+ }
+ }
+
+ void evictionTest()
+ {
+ Canvas2DLayerManager& manager = Canvas2DLayerManager::get();
+ manager.init(10, 5);
+ FakeCanvas2DLayerBridge layer;
+ layer.fakeFreeableBytes(10);
+ layer.storageAllocatedForRecordingChanged(8); // under the max
+ EXPECT_EQ(0, layer.m_freeMemoryIfPossibleCount);
+ layer.storageAllocatedForRecordingChanged(12); // over the max
+ EXPECT_EQ(1, layer.m_freeMemoryIfPossibleCount);
+ EXPECT_EQ((size_t)3, layer.m_freeableBytes);
+ EXPECT_EQ(0, layer.m_flushCount); // eviction succeeded without triggering a flush
+ EXPECT_EQ((size_t)5, layer.bytesAllocated());
+ }
+
+ void flushEvictionTest()
+ {
+ Canvas2DLayerManager& manager = Canvas2DLayerManager::get();
+ manager.init(10, 5);
+ FakeCanvas2DLayerBridge layer;
+ layer.fakeFreeableBytes(1); // Not enough freeable bytes, will cause aggressive eviction by flushing
+ layer.storageAllocatedForRecordingChanged(8); // under the max
+ EXPECT_EQ(0, layer.m_freeMemoryIfPossibleCount);
+ layer.storageAllocatedForRecordingChanged(12); // over the max
+ EXPECT_EQ(2, layer.m_freeMemoryIfPossibleCount); // Two tries, one before flush, one after flush
+ EXPECT_EQ((size_t)0, layer.m_freeableBytes);
+ EXPECT_EQ(1, layer.m_flushCount); // flush was attempted
+ EXPECT_EQ((size_t)11, layer.bytesAllocated()); // flush drops the layer from manager's tracking list
+ EXPECT_FALSE(manager.isInList(&layer));
+ }
+};
+
+namespace {
+
+TEST_F(Canvas2DLayerManagerTest, testStorageAllocationTracking)
+{
+ storageAllocationTrackingTest();
+}
+
+TEST_F(Canvas2DLayerManagerTest, testEviction)
+{
+ evictionTest();
+}
+
+TEST_F(Canvas2DLayerManagerTest, testFlushEviction)
+{
+ flushEvictionTest();
+}
+
+} // namespace
+