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 "BaseTileTexture.h"
28
29#include "BaseTile.h"
30#include "ClassTracker.h"
31#include "DeleteTextureOperation.h"
32#include "GLUtils.h"
33#include "TilesManager.h"
34
35#include <cutils/log.h>
36#include <wtf/text/CString.h>
37
38#undef XLOGC
39#define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "BaseTileTexture", __VA_ARGS__)
40
41#ifdef DEBUG
42
43#undef XLOG
44#define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "BaseTileTexture", __VA_ARGS__)
45
46#else
47
48#undef XLOG
49#define XLOG(...)
50
51#endif // DEBUG
52
53namespace WebCore {
54
55BaseTileTexture::BaseTileTexture(uint32_t w, uint32_t h)
56    : DoubleBufferedTexture(eglGetCurrentContext(),
57                            TilesManager::instance()->getSharedTextureMode())
58    , m_owner(0)
59    , m_busy(false)
60{
61    m_size.set(w, h);
62    m_ownTextureId = 0;
63
64    // Make sure they are created on the UI thread.
65    TilesManager::instance()->transferQueue()->initSharedSurfaceTextures(w, h);
66
67#ifdef DEBUG_COUNT
68    ClassTracker::instance()->increment("BaseTileTexture");
69#endif
70}
71
72BaseTileTexture::~BaseTileTexture()
73{
74    if (m_sharedTextureMode == EglImageMode) {
75        SharedTexture* textures[3] = { m_textureA, m_textureB, 0 };
76        destroyTextures(textures);
77    }
78#ifdef DEBUG_COUNT
79    ClassTracker::instance()->decrement("BaseTileTexture");
80#endif
81}
82
83void BaseTileTexture::requireGLTexture()
84{
85    if (!m_ownTextureId)
86        m_ownTextureId = GLUtils::createBaseTileGLTexture(m_size.width(), m_size.height());
87}
88
89void BaseTileTexture::discardGLTexture()
90{
91    if (m_ownTextureId)
92        GLUtils::deleteTexture(&m_ownTextureId);
93
94    if (m_owner) {
95        // clear both Tile->Texture and Texture->Tile links
96        m_owner->removeTexture(this);
97        release(m_owner);
98    }
99}
100
101void BaseTileTexture::destroyTextures(SharedTexture** textures)
102{
103    int x = 0;
104    while (textures[x]) {
105        // We need to delete the source texture and EGLImage in the texture
106        // generation thread. In theory we should be able to delete the EGLImage
107        // from either thread, but it currently throws an error if not deleted
108        // in the same EGLContext from which it was created.
109        textures[x]->lock();
110        DeleteTextureOperation* operation = new DeleteTextureOperation(
111            textures[x]->getSourceTextureId(), textures[x]->getEGLImage());
112        textures[x]->unlock();
113        TilesManager::instance()->scheduleOperation(operation);
114        x++;
115    }
116}
117
118TextureInfo* BaseTileTexture::producerLock()
119{
120    m_busyLock.lock();
121    m_busy = true;
122    m_busyLock.unlock();
123    return DoubleBufferedTexture::producerLock();
124}
125
126void BaseTileTexture::producerRelease()
127{
128    DoubleBufferedTexture::producerRelease();
129    setNotBusy();
130}
131
132void BaseTileTexture::producerReleaseAndSwap()
133{
134    DoubleBufferedTexture::producerReleaseAndSwap();
135    setNotBusy();
136}
137
138void BaseTileTexture::setNotBusy()
139{
140    android::Mutex::Autolock lock(m_busyLock);
141    m_busy = false;
142    m_busyCond.signal();
143}
144
145bool BaseTileTexture::busy()
146{
147    android::Mutex::Autolock lock(m_busyLock);
148    return m_busy;
149}
150
151void BaseTileTexture::producerUpdate(TextureInfo* textureInfo, const SkBitmap& bitmap)
152{
153    // no need to upload a texture since the bitmap is empty
154    if (!bitmap.width() && !bitmap.height()) {
155        producerRelease();
156        return;
157    }
158
159    // After the tiled layer checked in, this is not called anyway.
160    // TODO: cleanup the old code path for layer painting
161    // GLUtils::paintTextureWithBitmap(info, m_size, bitmap, 0, 0);
162
163    producerReleaseAndSwap();
164}
165
166bool BaseTileTexture::acquire(TextureOwner* owner, bool force)
167{
168    if (m_owner == owner)
169        return true;
170
171    return setOwner(owner, force);
172}
173
174bool BaseTileTexture::setOwner(TextureOwner* owner, bool force)
175{
176    // if the writable texture is busy (i.e. currently being written to) then we
177    // can't change the owner out from underneath that texture
178    m_busyLock.lock();
179    while (m_busy && force)
180        m_busyCond.wait(m_busyLock);
181    bool busy = m_busy;
182    m_busyLock.unlock();
183
184    if (!busy) {
185        // if we are not busy we can try to remove the texture from the layer;
186        // LayerAndroid::removeTexture() is protected by the same lock as
187        // LayerAndroid::paintBitmapGL(), so either we execute removeTexture()
188        // first and paintBitmapGL() will bail out, or we execute it after,
189        // and paintBitmapGL() will mark the texture as busy before
190        // relinquishing the lock. LayerAndroid::removeTexture() will call
191        // BaseTileTexture::release(), which will then do nothing
192        // if the texture is busy and we then don't return true.
193        bool proceed = true;
194        if (m_owner && m_owner != owner)
195            proceed = m_owner->removeTexture(this);
196
197        if (proceed) {
198            m_owner = owner;
199            return true;
200        }
201    }
202    return false;
203}
204
205bool BaseTileTexture::release(TextureOwner* owner)
206{
207    android::Mutex::Autolock lock(m_busyLock);
208    XLOG("texture %p releasing tile %p, m_owner %p, m_busy %d", this, owner, m_owner, m_busy);
209    if (m_owner != owner)
210        return false;
211
212    m_owner = 0;
213    return true;
214}
215
216void BaseTileTexture::setTile(TextureInfo* info, int x, int y,
217                                          float scale, TilePainter* painter,
218                                          unsigned int pictureCount)
219{
220    TextureTileInfo* textureInfo = m_texturesInfo.get(getWriteableTexture());
221    if (!textureInfo) {
222        textureInfo = new TextureTileInfo();
223    }
224    textureInfo->m_x = x;
225    textureInfo->m_y = y;
226    textureInfo->m_scale = scale;
227    textureInfo->m_painter = painter;
228    textureInfo->m_picture = pictureCount;
229    m_texturesInfo.set(getWriteableTexture(), textureInfo);
230}
231
232float BaseTileTexture::scale()
233{
234    TextureTileInfo* textureInfo = &m_ownTextureTileInfo;
235    return textureInfo->m_scale;
236}
237
238// This function + TilesManager::addItemInTransferQueue() is replacing the
239// setTile().
240void BaseTileTexture::setOwnTextureTileInfoFromQueue(const TextureTileInfo* info)
241{
242    m_ownTextureTileInfo.m_x = info->m_x;
243    m_ownTextureTileInfo.m_y = info->m_y;
244    m_ownTextureTileInfo.m_scale = info->m_scale;
245    m_ownTextureTileInfo.m_painter = info->m_painter;
246    m_ownTextureTileInfo.m_picture = info->m_picture;
247    m_ownTextureTileInfo.m_inverted = TilesManager::instance()->invertedScreen();
248    if (m_owner) {
249        BaseTile* owner = static_cast<BaseTile*>(m_owner);
250        owner->backTextureTransfer();
251    }
252
253}
254
255bool BaseTileTexture::readyFor(BaseTile* baseTile)
256{
257    const TextureTileInfo* info = &m_ownTextureTileInfo;
258    if (info &&
259        (info->m_x == baseTile->x()) &&
260        (info->m_y == baseTile->y()) &&
261        (info->m_scale == baseTile->scale()) &&
262        (info->m_painter == baseTile->painter()) &&
263        (info->m_inverted == TilesManager::instance()->invertedScreen()))
264        return true;
265
266    XLOG("texture %p readyFor return false for tile x, y (%d %d) texId %d ,"
267         " BaseTileTexture %p, BaseTile is %p, SCALE %f, painter %p, inv %d",
268         this, baseTile->x(), baseTile->y(), m_ownTextureId, this, baseTile,
269         baseTile->scale(), baseTile->painter(), TilesManager::instance()->invertedScreen());
270    return false;
271}
272
273} // namespace WebCore
274