1/*
2 * Copyright 2011, 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 "TransferQueue"
27#define LOG_NDEBUG 1
28
29#include "config.h"
30#include "TransferQueue.h"
31
32#if USE(ACCELERATED_COMPOSITING)
33
34#include "AndroidLog.h"
35#include "BaseRenderer.h"
36#include "DrawQuadData.h"
37#include "GLUtils.h"
38#include "Tile.h"
39#include "TileTexture.h"
40#include "TilesManager.h"
41#include <android/native_window.h>
42#include <gui/SurfaceTexture.h>
43#include <gui/SurfaceTextureClient.h>
44
45// For simple webView usage, MINIMAL_SIZE is recommended for memory saving.
46// In browser case, EFFICIENT_SIZE is preferred.
47#define MINIMAL_SIZE 1
48#define EFFICIENT_SIZE 6
49
50// Set this to 1 if we would like to take the new GpuUpload approach which
51// relied on the glCopyTexSubImage2D instead of a glDraw call
52#define GPU_UPLOAD_WITHOUT_DRAW 1
53
54namespace WebCore {
55
56TransferQueue::TransferQueue(bool useMinimalMem)
57    : m_eglSurface(EGL_NO_SURFACE)
58    , m_transferQueueIndex(0)
59    , m_fboID(0)
60    , m_sharedSurfaceTextureId(0)
61    , m_hasGLContext(true)
62    , m_currentDisplay(EGL_NO_DISPLAY)
63    , m_currentUploadType(DEFAULT_UPLOAD_TYPE)
64{
65    memset(&m_GLStateBeforeBlit, 0, sizeof(m_GLStateBeforeBlit));
66    m_transferQueueSize = useMinimalMem ? MINIMAL_SIZE : EFFICIENT_SIZE;
67    m_emptyItemCount = m_transferQueueSize;
68    m_transferQueue = new TileTransferData[m_transferQueueSize];
69}
70
71TransferQueue::~TransferQueue()
72{
73    android::Mutex::Autolock lock(m_transferQueueItemLocks);
74    cleanupGLResources();
75    delete[] m_transferQueue;
76}
77
78// Set the queue to be totally empty, abandon the Surface Texture. This should
79// be called only when we hit a wrong EGL Context in an error situation.
80void TransferQueue::resetQueue()
81{
82    android::Mutex::Autolock lock(m_transferQueueItemLocks);
83    emptyAndAbandonQueue();
84    m_sharedSurfaceTextureId = 0;
85}
86
87// This should be called within the m_transferQueueItemLocks.
88// Now only called by emptyQueue() and destructor.
89void TransferQueue::cleanupGLResources()
90{
91    if (m_fboID) {
92        glDeleteFramebuffers(1, &m_fboID);
93        m_fboID = 0;
94    }
95    if (m_sharedSurfaceTextureId) {
96        glDeleteTextures(1, &m_sharedSurfaceTextureId);
97        m_sharedSurfaceTextureId = 0;
98    }
99}
100
101void TransferQueue::initGLResources(int width, int height)
102{
103    android::Mutex::Autolock lock(m_transferQueueItemLocks);
104    if (!m_sharedSurfaceTextureId) {
105        glGenTextures(1, &m_sharedSurfaceTextureId);
106        sp<BufferQueue> bufferQueue(new BufferQueue(true));
107        m_sharedSurfaceTexture =
108#if GPU_UPLOAD_WITHOUT_DRAW
109            new android::SurfaceTexture(m_sharedSurfaceTextureId, true,
110                                        GL_TEXTURE_2D, true, bufferQueue);
111#else
112            new android::SurfaceTexture(m_sharedSurfaceTextureId, true,
113                                        GL_TEXTURE_EXTERNAL_OES, true,
114                                        bufferQueue);
115#endif
116        m_ANW = new android::SurfaceTextureClient(m_sharedSurfaceTexture);
117        m_sharedSurfaceTexture->setSynchronousMode(true);
118
119        int extraBuffersNeeded = 0;
120        int extraHackyBuffersNeeded = 0;
121        if (m_transferQueueSize == EFFICIENT_SIZE)
122            extraHackyBuffersNeeded = 13;
123        m_ANW->query(m_ANW.get(), NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
124                     &extraBuffersNeeded);
125        bufferQueue->setBufferCount(m_transferQueueSize + extraBuffersNeeded +
126                extraHackyBuffersNeeded);
127
128        int result = native_window_set_buffers_geometry(m_ANW.get(),
129                width, height, HAL_PIXEL_FORMAT_RGBA_8888);
130        GLUtils::checkSurfaceTextureError("native_window_set_buffers_geometry", result);
131        result = native_window_set_usage(m_ANW.get(),
132                GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
133        GLUtils::checkSurfaceTextureError("native_window_set_usage", result);
134    }
135
136    if (!m_fboID)
137        glGenFramebuffers(1, &m_fboID);
138}
139
140// When bliting, if the item from the transfer queue is mismatching b/t the
141// Tile and the content, then the item is considered as obsolete, and
142// the content is discarded.
143bool TransferQueue::checkObsolete(const TileTransferData* data)
144{
145    Tile* baseTilePtr = data->savedTilePtr;
146    if (!baseTilePtr) {
147        ALOGV("Invalid savedTilePtr , such that the tile is obsolete");
148        return true;
149    }
150
151    TileTexture* baseTileTexture = baseTilePtr->backTexture();
152    if (!baseTileTexture || baseTileTexture != data->savedTileTexturePtr) {
153        ALOGV("Invalid baseTileTexture %p (vs expected %p), such that the tile is obsolete",
154              baseTileTexture, data->savedTileTexturePtr);
155        return true;
156    }
157
158    return false;
159}
160
161void TransferQueue::blitTileFromQueue(GLuint fboID, TileTexture* destTex,
162                                      GLuint srcTexId, GLenum srcTexTarget,
163                                      int index)
164{
165#if GPU_UPLOAD_WITHOUT_DRAW
166    glBindFramebuffer(GL_FRAMEBUFFER, fboID);
167    glBindTexture(GL_TEXTURE_2D, destTex->m_ownTextureId);
168
169    int textureWidth = destTex->getSize().width();
170    int textureHeight = destTex->getSize().height();
171
172    glFramebufferTexture2D(GL_FRAMEBUFFER,
173                           GL_COLOR_ATTACHMENT0,
174                           GL_TEXTURE_2D,
175                           srcTexId,
176                           0);
177
178    glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0,
179                        textureWidth, textureHeight);
180    if (GLUtils::checkGlError("At the end of blitTileFromQueue()")) {
181#ifndef DEBUG
182        if (GLUtils::allowGLLog())
183#endif
184        ALOGE("blitTileFromQueue ERROR: fboId %d, destTexId %d, srcTexId %d,"
185              " textureWidth %d, textureHeight %d", fboID, destTex->m_ownTextureId,
186              srcTexId, textureWidth, textureHeight);
187    }
188#else
189    // Then set up the FBO and copy the SurfTex content in.
190    glBindFramebuffer(GL_FRAMEBUFFER, fboID);
191    glFramebufferTexture2D(GL_FRAMEBUFFER,
192                           GL_COLOR_ATTACHMENT0,
193                           GL_TEXTURE_2D,
194                           destTex->m_ownTextureId,
195                           0);
196    setGLStateForCopy(destTex->getSize().width(),
197                      destTex->getSize().height());
198    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
199    if (status != GL_FRAMEBUFFER_COMPLETE) {
200        ALOGV("Error: glCheckFramebufferStatus failed");
201        return;
202    }
203
204    // Use empty rect to set up the special matrix to draw.
205    SkRect rect  = SkRect::MakeEmpty();
206
207    TextureQuadData data(srcTexId, GL_NEAREST, srcTexTarget, Blit, 0, 0, 1.0, false);
208    TilesManager::instance()->shader()->drawQuad(&data);
209#endif
210}
211
212// This function must be called inside the m_transferQueueItemLocks, for the
213// wait and getHasGLContext().
214// Only called by updateQueueWithBitmap() for now.
215bool TransferQueue::readyForUpdate()
216{
217    if (!getHasGLContext())
218        return false;
219    // Don't use a while loop since when the WebView tear down, the emptyCount
220    // will still be 0, and we bailed out b/c of GL context lost.
221    if (!m_emptyItemCount)
222        m_transferQueueItemCond.wait(m_transferQueueItemLocks);
223
224    if (!getHasGLContext())
225        return false;
226
227    return true;
228}
229
230// Both getHasGLContext and setHasGLContext should be called within the lock.
231bool TransferQueue::getHasGLContext()
232{
233    return m_hasGLContext;
234}
235
236void TransferQueue::setHasGLContext(bool hasContext)
237{
238    m_hasGLContext = hasContext;
239}
240
241// Call within a m_transferQueueItemLocks, now called by resetQueue() and
242// cleanupGLResoucesAndQueue()
243void TransferQueue::emptyAndAbandonQueue()
244{
245    for (int i = 0 ; i < m_transferQueueSize; i++)
246        clearItemInTranferQueue(i);
247    m_emptyItemCount = m_transferQueueSize;
248    clearPureColorQueue();
249
250    if (m_sharedSurfaceTexture.get()) {
251        m_sharedSurfaceTexture->abandon();
252        m_sharedSurfaceTexture.clear();
253    }
254    // This can prevent the tex gen thread to produce, until next incoming draw.
255    setHasGLContext(false);
256}
257
258void TransferQueue::cleanupGLResourcesAndQueue()
259{
260    android::Mutex::Autolock lock(m_transferQueueItemLocks);
261    emptyAndAbandonQueue();
262    cleanupGLResources();
263}
264
265// Set all the content in the queue to pendingDiscard, after this, there will
266// be nothing added to the queue, and this can be called in any thread.
267// However, in order to discard the content in the Surface Texture using
268// updateTexImage, cleanupPendingDiscard need to be called on the UI thread.
269// Must be called within a m_transferQueueItemLocks.
270void TransferQueue::setPendingDiscard()
271{
272    for (int i = 0 ; i < m_transferQueueSize; i++)
273        if (m_transferQueue[i].status == pendingBlit)
274            m_transferQueue[i].status = pendingDiscard;
275
276    clearPureColorQueue();
277
278    bool GLContextExisted = getHasGLContext();
279    // Unblock the Tex Gen thread first before Tile Page deletion.
280    // Otherwise, there will be a deadlock while removing operations.
281    setHasGLContext(false);
282
283    // Only signal once when GL context lost.
284    if (GLContextExisted)
285        m_transferQueueItemCond.signal();
286}
287
288void TransferQueue::clearPureColorQueue()
289{
290    for (unsigned int i = 0 ; i < m_pureColorTileQueue.size(); i++) {
291        SkSafeUnref(m_pureColorTileQueue[i].savedTilePainter);
292        m_pureColorTileQueue[i].savedTilePainter = 0;
293    }
294    m_pureColorTileQueue.clear();
295}
296
297void TransferQueue::updatePureColorTiles()
298{
299    for (unsigned int i = 0 ; i < m_pureColorTileQueue.size(); i++) {
300        TileTransferData* data = &m_pureColorTileQueue[i];
301        if (data->status == pendingBlit) {
302            TileTexture* destTexture = 0;
303            bool obsoleteTile = checkObsolete(data);
304            if (!obsoleteTile) {
305                destTexture = data->savedTilePtr->backTexture();
306                destTexture->setPureColor(data->pureColor);
307                destTexture->transferComplete();
308            }
309        } else if (data->status == emptyItem || data->status == pendingDiscard) {
310            // The queue should be clear instead of setting to different status.
311            ALOGV("Warning: Don't expect an emptyItem here.");
312        }
313    }
314    clearPureColorQueue();
315}
316
317// Call on UI thread to copy from the shared Surface Texture to the Tile's texture.
318void TransferQueue::updateDirtyTiles()
319{
320    android::Mutex::Autolock lock(m_transferQueueItemLocks);
321
322    cleanupPendingDiscard();
323    if (!getHasGLContext())
324        setHasGLContext(true);
325
326    // Check the pure color tile first, since it is simpler.
327    updatePureColorTiles();
328
329    // Start from the oldest item, we call the updateTexImage to retrive
330    // the texture and blit that into each Tile's texture.
331    const int nextItemIndex = getNextTransferQueueIndex();
332    int index = nextItemIndex;
333    bool usedFboForUpload = false;
334    for (int k = 0; k < m_transferQueueSize ; k++) {
335        if (m_transferQueue[index].status == pendingBlit) {
336            bool obsoleteTile = checkObsolete(&m_transferQueue[index]);
337            // Save the needed info, update the Surf Tex, clean up the item in
338            // the queue. Then either move on to next item or copy the content.
339            TileTexture* destTexture = 0;
340            if (!obsoleteTile)
341                destTexture = m_transferQueue[index].savedTilePtr->backTexture();
342
343            if (m_transferQueue[index].uploadType == GpuUpload) {
344                status_t result = m_sharedSurfaceTexture->updateTexImage();
345                if (result != OK)
346                    ALOGE("unexpected error: updateTexImage return %d", result);
347            }
348
349            if (obsoleteTile) {
350                ALOGV("Warning: the texture is obsolete for this baseTile");
351                clearItemInTranferQueue(index);
352                index = (index + 1) % m_transferQueueSize;
353                continue;
354            }
355
356            // guarantee that we have a texture to blit into
357            destTexture->requireGLTexture();
358            GLUtils::checkGlError("before blitTileFromQueue");
359            if (m_transferQueue[index].uploadType == CpuUpload) {
360                // Here we just need to upload the bitmap content to the GL Texture
361                GLUtils::updateTextureWithBitmap(destTexture->m_ownTextureId,
362                                                 *m_transferQueue[index].bitmap);
363            } else {
364                if (!usedFboForUpload) {
365                    saveGLState();
366                    usedFboForUpload = true;
367                }
368                blitTileFromQueue(m_fboID, destTexture, m_sharedSurfaceTextureId,
369                                  m_sharedSurfaceTexture->getCurrentTextureTarget(),
370                                  index);
371            }
372
373            destTexture->setPure(false);
374            destTexture->transferComplete();
375            clearItemInTranferQueue(index);
376            ALOGV("Blit tile x, y %d %d with dest texture %p to destTexture->m_ownTextureId %d",
377                  m_transferQueue[index].savedTilePtr,
378                  destTexture,
379                  destTexture->m_ownTextureId);
380        }
381        index = (index + 1) % m_transferQueueSize;
382    }
383
384    // Clean up FBO setup. Doing this for both CPU/GPU upload can make the
385    // dynamic switch possible. Moving this out from the loop can save some
386    // milli-seconds.
387    if (usedFboForUpload) {
388        restoreGLState();
389        GLUtils::checkGlError("updateDirtyTiles");
390    }
391
392    m_emptyItemCount = m_transferQueueSize;
393    m_transferQueueItemCond.signal();
394}
395
396void TransferQueue::updateQueueWithBitmap(const TileRenderInfo* renderInfo,
397                                          SkBitmap& bitmap)
398{
399    TRACE_METHOD();
400    if (!tryUpdateQueueWithBitmap(renderInfo, bitmap)) {
401        // failed placing bitmap in queue, discard tile's texture so it will be
402        // re-enqueued (and repainted)
403        Tile* tile = renderInfo->baseTile;
404        if (tile)
405            tile->backTextureTransferFail();
406    }
407}
408
409bool TransferQueue::tryUpdateQueueWithBitmap(const TileRenderInfo* renderInfo,
410                                             SkBitmap& bitmap)
411{
412    // This lock need to cover the full update since it is possible that queue
413    // will be cleaned up in the middle of this update without the lock.
414    // The Surface Texture will not block us since the readyForUpdate will check
415    // availability of the slots in the queue first.
416    android::Mutex::Autolock lock(m_transferQueueItemLocks);
417    bool ready = readyForUpdate();
418    TextureUploadType currentUploadType = m_currentUploadType;
419    if (!ready) {
420        ALOGV("Quit bitmap update: not ready! for tile x y %d %d",
421              renderInfo->x, renderInfo->y);
422        return false;
423    }
424    if (currentUploadType == GpuUpload) {
425        // a) Dequeue the Surface Texture and write into the buffer
426        if (!m_ANW.get()) {
427            ALOGV("ERROR: ANW is null");
428            return false;
429        }
430
431        if (!GLUtils::updateSharedSurfaceTextureWithBitmap(m_ANW.get(), bitmap))
432            return false;
433    }
434
435    // b) After update the Surface Texture, now udpate the transfer queue info.
436    addItemInTransferQueue(renderInfo, currentUploadType, bitmap);
437
438    ALOGV("Bitmap updated x, y %d %d, baseTile %p",
439          renderInfo->x, renderInfo->y, renderInfo->baseTile);
440    return true;
441}
442
443void TransferQueue::addItemInPureColorQueue(const TileRenderInfo* renderInfo)
444{
445    // The pure color tiles' queue will be read from UI thread and written in
446    // Tex Gen thread, thus we need to have a lock here.
447    android::Mutex::Autolock lock(m_transferQueueItemLocks);
448    TileTransferData data;
449    addItemCommon(renderInfo, GpuUpload, &data);
450    data.pureColor = renderInfo->pureColor;
451    m_pureColorTileQueue.append(data);
452}
453
454void TransferQueue::clearItemInTranferQueue(int index)
455{
456    m_transferQueue[index].savedTilePtr = 0;
457    SkSafeUnref(m_transferQueue[index].savedTilePainter);
458    m_transferQueue[index].savedTilePainter = 0;
459    m_transferQueue[index].status = emptyItem;
460}
461
462// Translates the info from TileRenderInfo and others to TileTransferData.
463// This is used by pure color tiles and normal tiles.
464void TransferQueue::addItemCommon(const TileRenderInfo* renderInfo,
465                                  TextureUploadType type,
466                                  TileTransferData* data)
467{
468    data->savedTileTexturePtr = renderInfo->baseTile->backTexture();
469    data->savedTilePainter = renderInfo->tilePainter;
470    SkSafeRef(data->savedTilePainter);
471    data->savedTilePtr = renderInfo->baseTile;
472    data->status = pendingBlit;
473    data->uploadType = type;
474
475    IntRect inval(0, 0, 0, 0);
476}
477
478// Note that there should be lock/unlock around this function call.
479// Currently only called by GLUtils::updateSharedSurfaceTextureWithBitmap.
480void TransferQueue::addItemInTransferQueue(const TileRenderInfo* renderInfo,
481                                           TextureUploadType type,
482                                           SkBitmap& bitmap)
483{
484    m_transferQueueIndex = (m_transferQueueIndex + 1) % m_transferQueueSize;
485
486    int index = m_transferQueueIndex;
487    if (m_transferQueue[index].savedTilePtr
488        || m_transferQueue[index].status != emptyItem) {
489        ALOGV("ERROR update a tile which is dirty already @ index %d", index);
490    }
491
492    TileTransferData* data = &m_transferQueue[index];
493    addItemCommon(renderInfo, type, data);
494    if (type == CpuUpload) {
495        // Lazily create the bitmap
496        if (!m_transferQueue[index].bitmap) {
497            m_transferQueue[index].bitmap = new SkBitmap();
498            int w = bitmap.width();
499            int h = bitmap.height();
500            m_transferQueue[index].bitmap->setConfig(bitmap.config(), w, h);
501            m_transferQueue[index].bitmap->allocPixels();
502        }
503        SkBitmap temp = (*m_transferQueue[index].bitmap);
504        (*m_transferQueue[index].bitmap) = bitmap;
505        bitmap = temp;
506    }
507
508    m_emptyItemCount--;
509}
510
511void TransferQueue::setTextureUploadType(TextureUploadType type)
512{
513    android::Mutex::Autolock lock(m_transferQueueItemLocks);
514    if (m_currentUploadType == type)
515        return;
516
517    setPendingDiscard();
518
519    m_currentUploadType = type;
520    ALOGD("Now we set the upload to %s", m_currentUploadType == GpuUpload ? "GpuUpload" : "CpuUpload");
521}
522
523// Note: this need to be called within the lock and on the UI thread.
524// Only called by updateDirtyTiles() and emptyQueue() for now
525void TransferQueue::cleanupPendingDiscard()
526{
527    int index = getNextTransferQueueIndex();
528
529    for (int i = 0 ; i < m_transferQueueSize; i++) {
530        if (m_transferQueue[index].status == pendingDiscard) {
531            // No matter what the current upload type is, as long as there has
532            // been a Surf Tex enqueue operation, this updateTexImage need to
533            // be called to keep things in sync.
534            if (m_transferQueue[index].uploadType == GpuUpload) {
535                status_t result = m_sharedSurfaceTexture->updateTexImage();
536                if (result != OK)
537                    ALOGE("unexpected error: updateTexImage return %d", result);
538            }
539
540            // since tiles in the queue may be from another webview, remove
541            // their textures so that they will be repainted / retransferred
542            Tile* tile = m_transferQueue[index].savedTilePtr;
543            TileTexture* texture = m_transferQueue[index].savedTileTexturePtr;
544            if (tile && texture && texture->owner() == tile) {
545                // since tile destruction removes textures on the UI thread, the
546                // texture->owner ptr guarantees the tile is valid
547                tile->discardBackTexture();
548                ALOGV("transfer queue discarded tile %p, removed texture", tile);
549            }
550            clearItemInTranferQueue(index);
551        }
552        index = (index + 1) % m_transferQueueSize;
553    }
554}
555
556void TransferQueue::saveGLState()
557{
558    glGetIntegerv(GL_FRAMEBUFFER_BINDING, m_GLStateBeforeBlit.bufferId);
559    glGetIntegerv(GL_VIEWPORT, m_GLStateBeforeBlit.viewport);
560    glGetBooleanv(GL_SCISSOR_TEST, m_GLStateBeforeBlit.scissor);
561    glGetBooleanv(GL_DEPTH_TEST, m_GLStateBeforeBlit.depth);
562#ifdef DEBUG
563    glGetFloatv(GL_COLOR_CLEAR_VALUE, m_GLStateBeforeBlit.clearColor);
564#endif
565}
566
567void TransferQueue::setGLStateForCopy(int width, int height)
568{
569    // Need to match the texture size.
570    glViewport(0, 0, width, height);
571    glDisable(GL_SCISSOR_TEST);
572    glDisable(GL_DEPTH_TEST);
573    // Clear the content is only for debug purpose.
574#ifdef DEBUG
575    glClearColor(0, 0, 0, 0);
576    glClear(GL_COLOR_BUFFER_BIT);
577#endif
578}
579
580void TransferQueue::restoreGLState()
581{
582    glBindFramebuffer(GL_FRAMEBUFFER, m_GLStateBeforeBlit.bufferId[0]);
583    glViewport(m_GLStateBeforeBlit.viewport[0],
584               m_GLStateBeforeBlit.viewport[1],
585               m_GLStateBeforeBlit.viewport[2],
586               m_GLStateBeforeBlit.viewport[3]);
587
588    if (m_GLStateBeforeBlit.scissor[0])
589        glEnable(GL_SCISSOR_TEST);
590
591    if (m_GLStateBeforeBlit.depth[0])
592        glEnable(GL_DEPTH_TEST);
593#ifdef DEBUG
594    glClearColor(m_GLStateBeforeBlit.clearColor[0],
595                 m_GLStateBeforeBlit.clearColor[1],
596                 m_GLStateBeforeBlit.clearColor[2],
597                 m_GLStateBeforeBlit.clearColor[3]);
598#endif
599}
600
601int TransferQueue::getNextTransferQueueIndex()
602{
603    return (m_transferQueueIndex + 1) % m_transferQueueSize;
604}
605
606} // namespace WebCore
607
608#endif // USE(ACCELERATED_COMPOSITING
609