1/*
2 * Copyright 2010, The Android Open Source Project
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *  * Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 *  * 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 *
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#define LOG_TAG "TexturesGenerator"
27#define LOG_NDEBUG 1
28
29#include "config.h"
30#include "TexturesGenerator.h"
31
32#if USE(ACCELERATED_COMPOSITING)
33
34#include "AndroidLog.h"
35#include "BaseRenderer.h"
36#include "GLUtils.h"
37#include "PaintTileOperation.h"
38#include "TilesManager.h"
39#include "TransferQueue.h"
40
41namespace WebCore {
42
43TexturesGenerator::TexturesGenerator(TilesManager* instance)
44  : Thread(false)
45  , m_tilesManager(instance)
46  , m_deferredMode(false)
47  , m_renderer(0)
48{
49}
50
51TexturesGenerator::~TexturesGenerator()
52{
53    delete m_renderer;
54}
55
56bool TexturesGenerator::tryUpdateOperationWithPainter(Tile* tile, TilePainter* painter)
57{
58    android::Mutex::Autolock lock(mRequestedOperationsLock);
59    if (!mRequestedOperationsHash.contains(tile))
60        return false;
61
62    static_cast<PaintTileOperation*>(mRequestedOperationsHash.get(tile))->updatePainter(painter);
63    return true;
64}
65
66void TexturesGenerator::scheduleOperation(QueuedOperation* operation)
67{
68    bool signal = false;
69    {
70        android::Mutex::Autolock lock(mRequestedOperationsLock);
71        mRequestedOperations.append(operation);
72        mRequestedOperationsHash.set(operation->uniquePtr(), operation);
73
74        bool deferrable = operation->priority() >= gDeferPriorityCutoff;
75        m_deferredMode &= deferrable;
76
77        // signal if we weren't in deferred mode, or if we can no longer defer
78        signal = !m_deferredMode || !deferrable;
79    }
80    if (signal)
81        mRequestedOperationsCond.signal();
82}
83
84void TexturesGenerator::removeOperationsForFilter(OperationFilter* filter)
85{
86    if (!filter)
87        return;
88
89    android::Mutex::Autolock lock(mRequestedOperationsLock);
90    for (unsigned int i = 0; i < mRequestedOperations.size();) {
91        QueuedOperation* operation = mRequestedOperations[i];
92        if (filter->check(operation)) {
93            mRequestedOperations.remove(i);
94            mRequestedOperationsHash.remove(operation->uniquePtr());
95            delete operation;
96        } else {
97            i++;
98        }
99    }
100}
101
102status_t TexturesGenerator::readyToRun()
103{
104    m_renderer = BaseRenderer::createRenderer();
105    return NO_ERROR;
106}
107
108// Must be called from within a lock!
109QueuedOperation* TexturesGenerator::popNext()
110{
111    // Priority can change between when it was added and now
112    // Hence why the entire queue is rescanned
113    QueuedOperation* current = mRequestedOperations.last();
114    int currentPriority = current->priority();
115    if (currentPriority < 0) {
116        mRequestedOperations.removeLast();
117        mRequestedOperationsHash.remove(current->uniquePtr());
118        return current;
119    }
120    int currentIndex = mRequestedOperations.size() - 1;
121    // Scan from the back to make removing faster (less items to copy)
122    for (int i = mRequestedOperations.size() - 2; i >= 0; i--) {
123        QueuedOperation *next = mRequestedOperations[i];
124        int nextPriority = next->priority();
125        if (nextPriority < 0) {
126            // Found a very high priority item, go ahead and just handle it now
127            mRequestedOperations.remove(i);
128            mRequestedOperationsHash.remove(next->uniquePtr());
129            return next;
130        }
131        // pick items preferrably by priority, or if equal, by order of
132        // insertion (as we add items at the back of the queue)
133        if (nextPriority <= currentPriority) {
134            current = next;
135            currentPriority = nextPriority;
136            currentIndex = i;
137        }
138    }
139
140    if (!m_deferredMode && currentPriority >= gDeferPriorityCutoff) {
141        // finished with non-deferred rendering, enter deferred mode to wait
142        m_deferredMode = true;
143        return 0;
144    }
145
146    mRequestedOperations.remove(currentIndex);
147    mRequestedOperationsHash.remove(current->uniquePtr());
148    return current;
149}
150
151bool TexturesGenerator::threadLoop()
152{
153    // Check if we have any pending operations.
154    mRequestedOperationsLock.lock();
155
156    if (!m_deferredMode) {
157        // if we aren't currently deferring work, wait for new work to arrive
158        while (!mRequestedOperations.size())
159            mRequestedOperationsCond.wait(mRequestedOperationsLock);
160    } else {
161        // if we only have deferred work, wait for better work, or a timeout
162        mRequestedOperationsCond.waitRelative(mRequestedOperationsLock, gDeferNsecs);
163    }
164
165    mRequestedOperationsLock.unlock();
166
167    bool stop = false;
168    while (!stop) {
169        QueuedOperation* currentOperation = 0;
170
171        mRequestedOperationsLock.lock();
172        ALOGV("threadLoop, %d operations in the queue", mRequestedOperations.size());
173
174        if (mRequestedOperations.size())
175            currentOperation = popNext();
176        mRequestedOperationsLock.unlock();
177
178        if (currentOperation) {
179            ALOGV("threadLoop, painting the request with priority %d",
180                  currentOperation->priority());
181            // swap out the renderer if necessary
182            BaseRenderer::swapRendererIfNeeded(m_renderer);
183            currentOperation->run(m_renderer);
184        }
185
186        mRequestedOperationsLock.lock();
187        if (m_deferredMode && !currentOperation)
188            stop = true;
189        if (!mRequestedOperations.size()) {
190            m_deferredMode = false;
191            stop = true;
192        }
193        mRequestedOperationsLock.unlock();
194
195        if (currentOperation)
196            delete currentOperation; // delete outside lock
197    }
198    ALOGV("threadLoop empty");
199
200    return true;
201}
202
203} // namespace WebCore
204
205#endif // USE(ACCELERATED_COMPOSITING)
206