OSDN Git Service

Set the exact buffer size to Surf Tex
[android-x86/external-webkit.git] / Source / WebCore / platform / graphics / android / TransferQueue.cpp
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 #include "config.h"
27 #include "TransferQueue.h"
28
29 #if USE(ACCELERATED_COMPOSITING)
30
31 #include "BaseTile.h"
32 #include "PaintedSurface.h"
33 #include <android/native_window.h>
34 #include <gui/SurfaceTexture.h>
35 #include <gui/SurfaceTextureClient.h>
36
37 #include <cutils/log.h>
38 #include <wtf/text/CString.h>
39 #define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "TransferQueue", __VA_ARGS__)
40
41 #ifdef DEBUG
42
43 #undef XLOG
44 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "TransferQueue", __VA_ARGS__)
45
46 #else
47
48 #undef XLOG
49 #define XLOG(...)
50
51 #endif // DEBUG
52
53 #define ST_BUFFER_NUMBER 4
54
55 // Set this to 1 if we would like to take the new GpuUpload approach which
56 // relied on the glCopyTexSubImage2D instead of a glDraw call
57 #define GPU_UPLOAD_WITHOUT_DRAW 1
58
59 namespace WebCore {
60
61 TransferQueue::TransferQueue()
62     : m_eglSurface(EGL_NO_SURFACE)
63     , m_transferQueueIndex(0)
64     , m_fboID(0)
65     , m_sharedSurfaceTextureId(0)
66     , m_hasGLContext(true)
67     , m_interruptedByRemovingOp(false)
68     , m_currentDisplay(EGL_NO_DISPLAY)
69     , m_currentUploadType(DEFAULT_UPLOAD_TYPE)
70 {
71     memset(&m_GLStateBeforeBlit, 0, sizeof(m_GLStateBeforeBlit));
72
73     m_emptyItemCount = ST_BUFFER_NUMBER;
74
75     m_transferQueue = new TileTransferData[ST_BUFFER_NUMBER];
76 }
77
78 TransferQueue::~TransferQueue()
79 {
80     glDeleteFramebuffers(1, &m_fboID);
81     m_fboID = 0;
82     glDeleteTextures(1, &m_sharedSurfaceTextureId);
83     m_sharedSurfaceTextureId = 0;
84
85     delete[] m_transferQueue;
86 }
87
88 void TransferQueue::initSharedSurfaceTextures(int width, int height)
89 {
90     if (!m_sharedSurfaceTextureId) {
91         glGenTextures(1, &m_sharedSurfaceTextureId);
92         m_sharedSurfaceTexture =
93 #if GPU_UPLOAD_WITHOUT_DRAW
94             new android::SurfaceTexture(m_sharedSurfaceTextureId, true, GL_TEXTURE_2D);
95 #else
96             new android::SurfaceTexture(m_sharedSurfaceTextureId);
97 #endif
98         m_ANW = new android::SurfaceTextureClient(m_sharedSurfaceTexture);
99         m_sharedSurfaceTexture->setSynchronousMode(true);
100         m_sharedSurfaceTexture->setBufferCount(ST_BUFFER_NUMBER);
101
102         int result = native_window_set_buffers_geometry(m_ANW.get(),
103                 width, height, HAL_PIXEL_FORMAT_RGBA_8888);
104         GLUtils::checkSurfaceTextureError("native_window_set_buffers_geometry", result);
105         result = native_window_set_usage(m_ANW.get(),
106                 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
107         GLUtils::checkSurfaceTextureError("native_window_set_usage", result);
108     }
109
110     if (!m_fboID)
111         glGenFramebuffers(1, &m_fboID);
112 }
113
114 // When bliting, if the item from the transfer queue is mismatching b/t the
115 // BaseTile and the content, then the item is considered as obsolete, and
116 // the content is discarded.
117 bool TransferQueue::checkObsolete(int index)
118 {
119     BaseTile* baseTilePtr = m_transferQueue[index].savedBaseTilePtr;
120     if (!baseTilePtr) {
121         XLOG("Invalid savedBaseTilePtr , such that the tile is obsolete");
122         return true;
123     }
124
125     BaseTileTexture* baseTileTexture = baseTilePtr->backTexture();
126     if (!baseTileTexture) {
127         XLOG("Invalid baseTileTexture , such that the tile is obsolete");
128         return true;
129     }
130
131     const TextureTileInfo* tileInfo = &m_transferQueue[index].tileInfo;
132
133     if (tileInfo->m_x != baseTilePtr->x()
134         || tileInfo->m_y != baseTilePtr->y()
135         || tileInfo->m_scale != baseTilePtr->scale()
136         || tileInfo->m_painter != baseTilePtr->painter()) {
137         XLOG("Mismatching x, y, scale or painter , such that the tile is obsolete");
138         return true;
139     }
140
141     return false;
142 }
143
144 void TransferQueue::blitTileFromQueue(GLuint fboID, BaseTileTexture* destTex,
145                                       GLuint srcTexId, GLenum srcTexTarget,
146                                       int index)
147 {
148 #if GPU_UPLOAD_WITHOUT_DRAW
149     glBindFramebuffer(GL_FRAMEBUFFER, fboID);
150     glFramebufferTexture2D(GL_FRAMEBUFFER,
151                            GL_COLOR_ATTACHMENT0,
152                            GL_TEXTURE_2D,
153                            srcTexId,
154                            0);
155     glBindTexture(GL_TEXTURE_2D, destTex->m_ownTextureId);
156     glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0,
157                         destTex->getSize().width(),
158                         destTex->getSize().height());
159 #else
160     // Then set up the FBO and copy the SurfTex content in.
161     glBindFramebuffer(GL_FRAMEBUFFER, fboID);
162     glFramebufferTexture2D(GL_FRAMEBUFFER,
163                            GL_COLOR_ATTACHMENT0,
164                            GL_TEXTURE_2D,
165                            destTex->m_ownTextureId,
166                            0);
167     setGLStateForCopy(destTex->getSize().width(),
168                       destTex->getSize().height());
169     GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
170     if (status != GL_FRAMEBUFFER_COMPLETE) {
171         XLOG("Error: glCheckFramebufferStatus failed");
172         glBindFramebuffer(GL_FRAMEBUFFER, 0);
173         return;
174     }
175
176     // Use empty rect to set up the special matrix to draw.
177     SkRect rect  = SkRect::MakeEmpty();
178     TilesManager::instance()->shader()->drawQuad(rect, srcTexId, 1.0,
179                        srcTexTarget, GL_NEAREST);
180
181     // To workaround a sync issue on some platforms, we should insert the sync
182     // here while in the current FBO.
183     // This will essentially kick off the GPU command buffer, and the Tex Gen
184     // thread will then have to wait for this buffer to finish before writing
185     // into the same memory.
186     EGLDisplay dpy = eglGetCurrentDisplay();
187     if (m_currentDisplay != dpy)
188         m_currentDisplay = dpy;
189     if (m_currentDisplay != EGL_NO_DISPLAY) {
190         if (m_transferQueue[index].m_syncKHR != EGL_NO_SYNC_KHR)
191             eglDestroySyncKHR(m_currentDisplay, m_transferQueue[index].m_syncKHR);
192         m_transferQueue[index].m_syncKHR = eglCreateSyncKHR(m_currentDisplay,
193                                                             EGL_SYNC_FENCE_KHR,
194                                                             0);
195     }
196     GLUtils::checkEglError("CreateSyncKHR");
197 #endif
198 }
199
200 void TransferQueue::interruptTransferQueue(bool interrupt)
201 {
202     m_transferQueueItemLocks.lock();
203     m_interruptedByRemovingOp = interrupt;
204     if (m_interruptedByRemovingOp)
205         m_transferQueueItemCond.signal();
206     m_transferQueueItemLocks.unlock();
207 }
208
209 // This function must be called inside the m_transferQueueItemLocks, for the
210 // wait, m_interruptedByRemovingOp and getHasGLContext().
211 // Only called by updateQueueWithBitmap() for now.
212 bool TransferQueue::readyForUpdate()
213 {
214     if (!getHasGLContext())
215         return false;
216     // Don't use a while loop since when the WebView tear down, the emptyCount
217     // will still be 0, and we bailed out b/c of GL context lost.
218     if (!m_emptyItemCount) {
219         if (m_interruptedByRemovingOp)
220             return false;
221         m_transferQueueItemCond.wait(m_transferQueueItemLocks);
222         if (m_interruptedByRemovingOp)
223             return false;
224     }
225
226     if (!getHasGLContext())
227         return false;
228
229     // Disable this wait until we figure out why this didn't work on some
230     // drivers b/5332112.
231 #if 0
232     if (m_currentUploadType == GpuUpload
233         && m_currentDisplay != EGL_NO_DISPLAY) {
234         // Check the GPU fence
235         EGLSyncKHR syncKHR = m_transferQueue[getNextTransferQueueIndex()].m_syncKHR;
236         if (syncKHR != EGL_NO_SYNC_KHR)
237             eglClientWaitSyncKHR(m_currentDisplay,
238                                  syncKHR,
239                                  EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
240                                  EGL_FOREVER_KHR);
241     }
242     GLUtils::checkEglError("WaitSyncKHR");
243 #endif
244
245     return true;
246 }
247
248 // Both getHasGLContext and setHasGLContext should be called within the lock.
249 bool TransferQueue::getHasGLContext()
250 {
251     return m_hasGLContext;
252 }
253
254 void TransferQueue::setHasGLContext(bool hasContext)
255 {
256     m_hasGLContext = hasContext;
257 }
258
259 // Only called when WebView is destroyed or switching the uploadType.
260 void TransferQueue::discardQueue()
261 {
262     android::Mutex::Autolock lock(m_transferQueueItemLocks);
263
264     for (int i = 0 ; i < ST_BUFFER_NUMBER; i++)
265         if (m_transferQueue[i].status == pendingBlit)
266             m_transferQueue[i].status = pendingDiscard;
267
268     bool GLContextExisted = getHasGLContext();
269     // Unblock the Tex Gen thread first before Tile Page deletion.
270     // Otherwise, there will be a deadlock while removing operations.
271     setHasGLContext(false);
272
273     // Only signal once when GL context lost.
274     if (GLContextExisted)
275         m_transferQueueItemCond.signal();
276 }
277
278 // Call on UI thread to copy from the shared Surface Texture to the BaseTile's texture.
279 void TransferQueue::updateDirtyBaseTiles()
280 {
281     android::Mutex::Autolock lock(m_transferQueueItemLocks);
282
283     cleanupTransportQueue();
284     if (!getHasGLContext())
285         setHasGLContext(true);
286
287     // Start from the oldest item, we call the updateTexImage to retrive
288     // the texture and blit that into each BaseTile's texture.
289     const int nextItemIndex = getNextTransferQueueIndex();
290     int index = nextItemIndex;
291     bool usedFboForUpload = false;
292     for (int k = 0; k < ST_BUFFER_NUMBER ; k++) {
293         if (m_transferQueue[index].status == pendingBlit) {
294             bool obsoleteBaseTile = checkObsolete(index);
295             // Save the needed info, update the Surf Tex, clean up the item in
296             // the queue. Then either move on to next item or copy the content.
297             BaseTileTexture* destTexture = 0;
298             if (!obsoleteBaseTile)
299                 destTexture = m_transferQueue[index].savedBaseTilePtr->backTexture();
300             if (m_transferQueue[index].uploadType == GpuUpload) {
301                 status_t result = m_sharedSurfaceTexture->updateTexImage();
302                 if (result != OK)
303                     XLOGC("unexpected error: updateTexImage return %d", result);
304             }
305             m_transferQueue[index].savedBaseTilePtr = 0;
306             m_transferQueue[index].status = emptyItem;
307             if (obsoleteBaseTile) {
308                 XLOG("Warning: the texture is obsolete for this baseTile");
309                 index = (index + 1) % ST_BUFFER_NUMBER;
310                 continue;
311             }
312
313             // guarantee that we have a texture to blit into
314             destTexture->requireGLTexture();
315
316             if (m_transferQueue[index].uploadType == CpuUpload) {
317                 // Here we just need to upload the bitmap content to the GL Texture
318                 GLUtils::updateTextureWithBitmap(destTexture->m_ownTextureId, 0, 0,
319                                                  *m_transferQueue[index].bitmap);
320             } else {
321                 if (!usedFboForUpload) {
322                     saveGLState();
323                     usedFboForUpload = true;
324                 }
325                 blitTileFromQueue(m_fboID, destTexture,
326                                   m_sharedSurfaceTextureId,
327                                   m_sharedSurfaceTexture->getCurrentTextureTarget(),
328                                   index);
329             }
330
331             // After the base tile copied into the GL texture, we need to
332             // update the texture's info such that at draw time, readyFor
333             // will find the latest texture's info
334             // We don't need a map any more, each texture contains its own
335             // texturesTileInfo.
336             destTexture->setOwnTextureTileInfoFromQueue(&m_transferQueue[index].tileInfo);
337
338             XLOG("Blit tile x, y %d %d with dest texture %p to destTexture->m_ownTextureId %d",
339                  m_transferQueue[index].tileInfo.m_x,
340                  m_transferQueue[index].tileInfo.m_y,
341                  destTexture,
342                  destTexture->m_ownTextureId);
343         }
344         index = (index + 1) % ST_BUFFER_NUMBER;
345     }
346
347     // Clean up FBO setup. Doing this for both CPU/GPU upload can make the
348     // dynamic switch possible. Moving this out from the loop can save some
349     // milli-seconds.
350     if (usedFboForUpload) {
351         glBindFramebuffer(GL_FRAMEBUFFER, 0); // rebind the standard FBO
352         restoreGLState();
353         GLUtils::checkGlError("updateDirtyBaseTiles");
354     }
355
356     m_emptyItemCount = ST_BUFFER_NUMBER;
357     m_transferQueueItemCond.signal();
358 }
359
360 void TransferQueue::updateQueueWithBitmap(const TileRenderInfo* renderInfo,
361                                           int x, int y, const SkBitmap& bitmap)
362 {
363     if (!tryUpdateQueueWithBitmap(renderInfo, x, y, bitmap)) {
364         // failed placing bitmap in queue, discard tile's texture so it will be
365         // re-enqueued (and repainted)
366         BaseTile* tile = renderInfo->baseTile;
367         if (tile)
368             tile->backTextureTransferFail();
369     }
370 }
371
372 bool TransferQueue::tryUpdateQueueWithBitmap(const TileRenderInfo* renderInfo,
373                                           int x, int y, const SkBitmap& bitmap)
374 {
375     m_transferQueueItemLocks.lock();
376     bool ready = readyForUpdate();
377     TextureUploadType currentUploadType = m_currentUploadType;
378     m_transferQueueItemLocks.unlock();
379     if (!ready) {
380         XLOG("Quit bitmap update: not ready! for tile x y %d %d",
381              renderInfo->x, renderInfo->y);
382         return false;
383     }
384     if (currentUploadType == GpuUpload) {
385         // a) Dequeue the Surface Texture and write into the buffer
386         if (!m_ANW.get()) {
387             XLOG("ERROR: ANW is null");
388             return false;
389         }
390
391         ANativeWindow_Buffer buffer;
392         if (ANativeWindow_lock(m_ANW.get(), &buffer, 0))
393             return false;
394
395         uint8_t* img = (uint8_t*)buffer.bits;
396         int row, col;
397         int bpp = 4; // Now we only deal with RGBA8888 format.
398         int width = TilesManager::instance()->tileWidth();
399         int height = TilesManager::instance()->tileHeight();
400         if (!x && !y && bitmap.width() == width && bitmap.height() == height) {
401             bitmap.lockPixels();
402             uint8_t* bitmapOrigin = static_cast<uint8_t*>(bitmap.getPixels());
403             if (buffer.stride != bitmap.width())
404                 // Copied line by line since we need to handle the offsets and stride.
405                 for (row = 0 ; row < bitmap.height(); row ++) {
406                     uint8_t* dst = &(img[buffer.stride * row * bpp]);
407                     uint8_t* src = &(bitmapOrigin[bitmap.width() * row * bpp]);
408                     memcpy(dst, src, bpp * bitmap.width());
409                 }
410             else
411                 memcpy(img, bitmapOrigin, bpp * bitmap.width() * bitmap.height());
412
413             bitmap.unlockPixels();
414         } else {
415             // TODO: implement the partial invalidate here!
416             XLOG("ERROR: don't expect to get here yet before we support partial inval");
417         }
418
419         ANativeWindow_unlockAndPost(m_ANW.get());
420     }
421
422     m_transferQueueItemLocks.lock();
423     // b) After update the Surface Texture, now udpate the transfer queue info.
424     addItemInTransferQueue(renderInfo, currentUploadType, &bitmap);
425
426     m_transferQueueItemLocks.unlock();
427     XLOG("Bitmap updated x, y %d %d, baseTile %p",
428          renderInfo->x, renderInfo->y, renderInfo->baseTile);
429     return true;
430 }
431
432 // Note that there should be lock/unlock around this function call.
433 // Currently only called by GLUtils::updateSharedSurfaceTextureWithBitmap.
434 void TransferQueue::addItemInTransferQueue(const TileRenderInfo* renderInfo,
435                                            TextureUploadType type,
436                                            const SkBitmap* bitmap)
437 {
438     m_transferQueueIndex = (m_transferQueueIndex + 1) % ST_BUFFER_NUMBER;
439
440     int index = m_transferQueueIndex;
441     if (m_transferQueue[index].savedBaseTilePtr
442         || m_transferQueue[index].status != emptyItem) {
443         XLOG("ERROR update a tile which is dirty already @ index %d", index);
444     }
445
446     m_transferQueue[index].savedBaseTileTexturePtr = renderInfo->baseTile->backTexture();
447     m_transferQueue[index].savedBaseTilePtr = renderInfo->baseTile;
448     m_transferQueue[index].status = pendingBlit;
449     m_transferQueue[index].uploadType = type;
450     if (type == CpuUpload && bitmap) {
451         // Lazily create the bitmap
452         if (!m_transferQueue[index].bitmap) {
453             m_transferQueue[index].bitmap = new SkBitmap();
454             int w = bitmap->width();
455             int h = bitmap->height();
456             m_transferQueue[index].bitmap->setConfig(bitmap->config(), w, h);
457         }
458         bitmap->copyTo(m_transferQueue[index].bitmap, bitmap->config());
459     }
460
461     // Now fill the tileInfo.
462     TextureTileInfo* textureInfo = &m_transferQueue[index].tileInfo;
463
464     textureInfo->m_x = renderInfo->x;
465     textureInfo->m_y = renderInfo->y;
466     textureInfo->m_scale = renderInfo->scale;
467     textureInfo->m_painter = renderInfo->tilePainter;
468
469     textureInfo->m_picture = renderInfo->textureInfo->m_pictureCount;
470
471     m_emptyItemCount--;
472 }
473
474 void TransferQueue::setTextureUploadType(TextureUploadType type)
475 {
476     if (m_currentUploadType == type)
477         return;
478
479     discardQueue();
480
481     android::Mutex::Autolock lock(m_transferQueueItemLocks);
482     m_currentUploadType = type;
483     XLOGC("Now we set the upload to %s", m_currentUploadType == GpuUpload ? "GpuUpload" : "CpuUpload");
484 }
485
486 // Note: this need to be called within th lock.
487 // Only called by updateDirtyBaseTiles() for now
488 void TransferQueue::cleanupTransportQueue()
489 {
490     int index = getNextTransferQueueIndex();
491
492     for (int i = 0 ; i < ST_BUFFER_NUMBER; i++) {
493         if (m_transferQueue[index].status == pendingDiscard) {
494             // No matter what the current upload type is, as long as there has
495             // been a Surf Tex enqueue operation, this updateTexImage need to
496             // be called to keep things in sync.
497             if (m_transferQueue[index].uploadType == GpuUpload) {
498                 status_t result = m_sharedSurfaceTexture->updateTexImage();
499                 if (result != OK)
500                     XLOGC("unexpected error: updateTexImage return %d", result);
501             }
502
503             // since tiles in the queue may be from another webview, remove
504             // their textures so that they will be repainted / retransferred
505             BaseTile* tile = m_transferQueue[index].savedBaseTilePtr;
506             BaseTileTexture* texture = m_transferQueue[index].savedBaseTileTexturePtr;
507             if (tile && texture && texture->owner() == tile) {
508                 // since tile destruction removes textures on the UI thread, the
509                 // texture->owner ptr guarantees the tile is valid
510                 tile->discardBackTexture();
511                 XLOG("transfer queue discarded tile %p, removed texture", tile);
512             }
513
514             m_transferQueue[index].savedBaseTilePtr = 0;
515             m_transferQueue[index].savedBaseTileTexturePtr = 0;
516             m_transferQueue[index].status = emptyItem;
517         }
518         index = (index + 1) % ST_BUFFER_NUMBER;
519     }
520 }
521
522 void TransferQueue::saveGLState()
523 {
524     glGetIntegerv(GL_VIEWPORT, m_GLStateBeforeBlit.viewport);
525     glGetBooleanv(GL_SCISSOR_TEST, m_GLStateBeforeBlit.scissor);
526     glGetBooleanv(GL_DEPTH_TEST, m_GLStateBeforeBlit.depth);
527 #if DEBUG
528     glGetFloatv(GL_COLOR_CLEAR_VALUE, m_GLStateBeforeBlit.clearColor);
529 #endif
530 }
531
532 void TransferQueue::setGLStateForCopy(int width, int height)
533 {
534     // Need to match the texture size.
535     glViewport(0, 0, width, height);
536     glDisable(GL_SCISSOR_TEST);
537     glDisable(GL_DEPTH_TEST);
538     // Clear the content is only for debug purpose.
539 #if DEBUG
540     glClearColor(0, 0, 0, 0);
541     glClear(GL_COLOR_BUFFER_BIT);
542 #endif
543 }
544
545 void TransferQueue::restoreGLState()
546 {
547     glViewport(m_GLStateBeforeBlit.viewport[0],
548                m_GLStateBeforeBlit.viewport[1],
549                m_GLStateBeforeBlit.viewport[2],
550                m_GLStateBeforeBlit.viewport[3]);
551
552     if (m_GLStateBeforeBlit.scissor[0])
553         glEnable(GL_SCISSOR_TEST);
554
555     if (m_GLStateBeforeBlit.depth[0])
556         glEnable(GL_DEPTH_TEST);
557 #if DEBUG
558     glClearColor(m_GLStateBeforeBlit.clearColor[0],
559                  m_GLStateBeforeBlit.clearColor[1],
560                  m_GLStateBeforeBlit.clearColor[2],
561                  m_GLStateBeforeBlit.clearColor[3]);
562 #endif
563 }
564
565 int TransferQueue::getNextTransferQueueIndex()
566 {
567     return (m_transferQueueIndex + 1) % ST_BUFFER_NUMBER;
568 }
569
570 } // namespace WebCore
571
572 #endif // USE(ACCELERATED_COMPOSITING