1/*
2Copyright (C) 2012 Google Inc. All rights reserved.
3
4Redistribution and use in source and binary forms, with or without
5modification, are permitted provided that the following conditions
6are met:
71.  Redistributions of source code must retain the above copyright
8    notice, this list of conditions and the following disclaimer.
92.  Redistributions in binary form must reproduce the above copyright
10    notice, this list of conditions and the following disclaimer in the
11    documentation and/or other materials provided with the distribution.
12
13THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23*/
24#include "config.h"
25
26#include "platform/graphics/Canvas2DLayerManager.h"
27
28#include "public/platform/Platform.h"
29#include "wtf/StdLibExtras.h"
30
31namespace {
32
33enum {
34    DefaultMaxBytesAllocated = 64*1024*1024,
35    DefaultTargetBytesAllocated = 16*1024*1024,
36};
37
38} // unnamed namespace
39
40namespace blink {
41
42Canvas2DLayerManager::Canvas2DLayerManager()
43    : m_bytesAllocated(0)
44    , m_maxBytesAllocated(DefaultMaxBytesAllocated)
45    , m_targetBytesAllocated(DefaultTargetBytesAllocated)
46    , m_taskObserverActive(false)
47{
48}
49
50Canvas2DLayerManager::~Canvas2DLayerManager()
51{
52    ASSERT(!m_bytesAllocated);
53    ASSERT(!m_layerList.head());
54    ASSERT(!m_taskObserverActive);
55}
56
57void Canvas2DLayerManager::init(size_t maxBytesAllocated, size_t targetBytesAllocated)
58{
59    ASSERT(maxBytesAllocated >= targetBytesAllocated);
60    m_maxBytesAllocated = maxBytesAllocated;
61    m_targetBytesAllocated = targetBytesAllocated;
62    if (m_taskObserverActive) {
63        Platform::current()->currentThread()->removeTaskObserver(this);
64        m_taskObserverActive = false;
65    }
66}
67
68Canvas2DLayerManager& Canvas2DLayerManager::get()
69{
70    DEFINE_STATIC_LOCAL(Canvas2DLayerManager, manager, ());
71    return manager;
72}
73
74void Canvas2DLayerManager::willProcessTask()
75{
76}
77
78void Canvas2DLayerManager::didProcessTask()
79{
80    // Called after the script action for the current frame has been processed.
81    ASSERT(m_taskObserverActive);
82    Platform::current()->currentThread()->removeTaskObserver(this);
83    m_taskObserverActive = false;
84    Canvas2DLayerBridge* layer = m_layerList.head();
85    while (layer) {
86        Canvas2DLayerBridge* currentLayer = layer;
87        // must increment iterator before calling limitPendingFrames, which
88        // may result in the layer being removed from the list.
89        layer = layer->next();
90        currentLayer->limitPendingFrames();
91    }
92}
93
94void Canvas2DLayerManager::layerDidDraw(Canvas2DLayerBridge* layer)
95{
96    if (isInList(layer)) {
97        if (layer != m_layerList.head()) {
98            m_layerList.remove(layer);
99            m_layerList.push(layer); // Set as MRU
100        }
101    }
102
103    if (!m_taskObserverActive) {
104        m_taskObserverActive = true;
105        // Schedule a call to didProcessTask() after completion of the current script task.
106        Platform::current()->currentThread()->addTaskObserver(this);
107    }
108}
109
110void Canvas2DLayerManager::layerTransientResourceAllocationChanged(Canvas2DLayerBridge* layer, intptr_t deltaBytes)
111{
112    ASSERT((intptr_t)m_bytesAllocated + deltaBytes >= 0);
113    m_bytesAllocated = (intptr_t)m_bytesAllocated + deltaBytes;
114    if (!isInList(layer) && layer->hasTransientResources()) {
115        m_layerList.push(layer);
116    } else if (isInList(layer) && !layer->hasTransientResources()) {
117        m_layerList.remove(layer);
118        layer->setNext(0);
119        layer->setPrev(0);
120    }
121
122    if (deltaBytes > 0)
123        freeMemoryIfNecessary();
124}
125
126void Canvas2DLayerManager::freeMemoryIfNecessary()
127{
128    if (m_bytesAllocated >= m_maxBytesAllocated) {
129        // Pass 1: Free memory from caches
130        Canvas2DLayerBridge* layer = m_layerList.tail(); // LRU
131        while (layer && m_bytesAllocated > m_targetBytesAllocated) {
132            Canvas2DLayerBridge* currentLayer = layer;
133            layer = layer->prev();
134            currentLayer->freeMemoryIfPossible(m_bytesAllocated - m_targetBytesAllocated);
135            ASSERT(isInList(currentLayer) == currentLayer->hasTransientResources());
136        }
137
138        // Pass 2: Flush canvases
139        layer = m_layerList.tail();
140        while (m_bytesAllocated > m_targetBytesAllocated && layer) {
141            Canvas2DLayerBridge* currentLayer = layer;
142            layer = layer->prev();
143            currentLayer->flush();
144            currentLayer->freeMemoryIfPossible(m_bytesAllocated - m_targetBytesAllocated);
145            ASSERT(isInList(currentLayer) == currentLayer->hasTransientResources());
146        }
147    }
148}
149
150bool Canvas2DLayerManager::isInList(Canvas2DLayerBridge* layer) const
151{
152    return layer->prev() || m_layerList.head() == layer;
153}
154
155} // namespace blink
156
157