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#include "config.h"
27#include "TexturesGenerator.h"
28
29#if USE(ACCELERATED_COMPOSITING)
30
31#include "BaseLayerAndroid.h"
32#include "GLUtils.h"
33#include "PaintTileOperation.h"
34#include "TilesManager.h"
35
36#ifdef DEBUG
37
38#include <cutils/log.h>
39#include <wtf/CurrentTime.h>
40#include <wtf/text/CString.h>
41
42#undef XLOG
43#define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "TexturesGenerator", __VA_ARGS__)
44
45#else
46
47#undef XLOG
48#define XLOG(...)
49
50#endif // DEBUG
51
52namespace WebCore {
53
54void TexturesGenerator::scheduleOperation(QueuedOperation* operation)
55{
56    {
57        android::Mutex::Autolock lock(mRequestedOperationsLock);
58        mRequestedOperations.append(operation);
59    }
60    mRequestedOperationsCond.signal();
61}
62
63void TexturesGenerator::removeOperationsForPage(TiledPage* page)
64{
65    removeOperationsForFilter(new PageFilter(page));
66}
67
68void TexturesGenerator::removePaintOperationsForPage(TiledPage* page, bool waitForRunning)
69{
70    removeOperationsForFilter(new PagePaintFilter(page), waitForRunning);
71}
72
73void TexturesGenerator::removeOperationsForFilter(OperationFilter* filter)
74{
75    removeOperationsForFilter(filter, true);
76}
77
78void TexturesGenerator::removeOperationsForFilter(OperationFilter* filter, bool waitForRunning)
79{
80    if (!filter)
81        return;
82
83    android::Mutex::Autolock lock(mRequestedOperationsLock);
84    for (unsigned int i = 0; i < mRequestedOperations.size();) {
85        QueuedOperation* operation = mRequestedOperations[i];
86        if (filter->check(operation)) {
87            mRequestedOperations.remove(i);
88            delete operation;
89        } else {
90            i++;
91        }
92    }
93
94    if (waitForRunning && m_currentOperation) {
95        QueuedOperation* operation = m_currentOperation;
96
97        if (operation && filter->check(operation)) {
98            m_waitForCompletion = true;
99            // The reason we are signaling the transferQueue is :
100            // TransferQueue may be waiting a slot to work on, but now UI
101            // thread is waiting for Tex Gen thread to finish first before the
102            // UI thread can free a slot for the transferQueue.
103            // Therefore, it could be a deadlock.
104            // The solution is use this as a flag to tell Tex Gen thread that
105            // UI thread is waiting now, Tex Gen thread should not wait for the
106            // queue any more.
107            TilesManager::instance()->transferQueue()->interruptTransferQueue(true);
108        }
109
110        delete filter;
111
112        // At this point, it means that we are currently executing an operation that
113        // we want to be removed -- we should wait until it is done, so that
114        // when we return our caller can be sure that there is no more operations
115        // in the queue matching the given filter.
116        while (m_waitForCompletion)
117            mRequestedOperationsCond.wait(mRequestedOperationsLock);
118    } else {
119        delete filter;
120    }
121}
122
123status_t TexturesGenerator::readyToRun()
124{
125    TilesManager::instance()->markGeneratorAsReady();
126    XLOG("Thread ready to run");
127    return NO_ERROR;
128}
129
130// Must be called from within a lock!
131QueuedOperation* TexturesGenerator::popNext()
132{
133    // Priority can change between when it was added and now
134    // Hence why the entire queue is rescanned
135    QueuedOperation* current = mRequestedOperations.last();
136    int currentPriority = current->priority();
137    if (currentPriority < 0) {
138        mRequestedOperations.removeLast();
139        return current;
140    }
141    int currentIndex = mRequestedOperations.size() - 1;
142    // Scan from the back to make removing faster (less items to copy)
143    for (int i = mRequestedOperations.size() - 2; i >= 0; i--) {
144        QueuedOperation *next = mRequestedOperations[i];
145        int nextPriority = next->priority();
146        if (nextPriority < 0) {
147            // Found a very high priority item, go ahead and just handle it now
148            mRequestedOperations.remove(i);
149            return next;
150        }
151        // pick items preferrably by priority, or if equal, by order of
152        // insertion (as we add items at the back of the queue)
153        if (nextPriority <= currentPriority) {
154            current = next;
155            currentPriority = nextPriority;
156            currentIndex = i;
157        }
158    }
159    mRequestedOperations.remove(currentIndex);
160    return current;
161}
162
163bool TexturesGenerator::threadLoop()
164{
165    // Check if we have any pending operations.
166    mRequestedOperationsLock.lock();
167    while (!mRequestedOperations.size())
168        mRequestedOperationsCond.wait(mRequestedOperationsLock);
169
170    XLOG("threadLoop, got signal");
171    mRequestedOperationsLock.unlock();
172
173    m_currentOperation = 0;
174    bool stop = false;
175    while (!stop) {
176        mRequestedOperationsLock.lock();
177        XLOG("threadLoop, %d operations in the queue", mRequestedOperations.size());
178        if (mRequestedOperations.size())
179            m_currentOperation = popNext();
180        mRequestedOperationsLock.unlock();
181
182        if (m_currentOperation) {
183            XLOG("threadLoop, painting the request with priority %d", m_currentOperation->priority());
184            m_currentOperation->run();
185        }
186
187        QueuedOperation* oldOperation = m_currentOperation;
188        mRequestedOperationsLock.lock();
189        if (m_currentOperation)
190            m_currentOperation = 0;
191        if (!mRequestedOperations.size())
192            stop = true;
193        if (m_waitForCompletion) {
194            m_waitForCompletion = false;
195            TilesManager::instance()->transferQueue()->interruptTransferQueue(false);
196            mRequestedOperationsCond.signal();
197        }
198        mRequestedOperationsLock.unlock();
199        if (oldOperation)
200            delete oldOperation; // delete outside lock
201    }
202    XLOG("threadLoop empty");
203
204    return true;
205}
206
207} // namespace WebCore
208
209#endif // USE(ACCELERATED_COMPOSITING)
210