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
TransferQueue()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
~TransferQueue()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
initSharedSurfaceTextures(int width,int height)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+1);
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.
checkObsolete(int index)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
blitTileFromQueue(GLuint fboID,BaseTileTexture * destTex,GLuint srcTexId,GLenum srcTexTarget,int index)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
interruptTransferQueue(bool interrupt)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.
readyForUpdate()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.
getHasGLContext()249 bool TransferQueue::getHasGLContext()
250 {
251 return m_hasGLContext;
252 }
253
setHasGLContext(bool hasContext)254 void TransferQueue::setHasGLContext(bool hasContext)
255 {
256 m_hasGLContext = hasContext;
257 }
258
259 // Only called when WebView is destroyed or switching the uploadType.
discardQueue()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.
updateDirtyBaseTiles()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 m_sharedSurfaceTexture->updateTexImage();
302 m_transferQueue[index].savedBaseTilePtr = 0;
303 m_transferQueue[index].status = emptyItem;
304 if (obsoleteBaseTile) {
305 XLOG("Warning: the texture is obsolete for this baseTile");
306 index = (index + 1) % ST_BUFFER_NUMBER;
307 continue;
308 }
309
310 // guarantee that we have a texture to blit into
311 destTexture->requireGLTexture();
312
313 if (m_transferQueue[index].uploadType == CpuUpload) {
314 // Here we just need to upload the bitmap content to the GL Texture
315 GLUtils::updateTextureWithBitmap(destTexture->m_ownTextureId, 0, 0,
316 *m_transferQueue[index].bitmap);
317 } else {
318 if (!usedFboForUpload) {
319 saveGLState();
320 usedFboForUpload = true;
321 }
322 blitTileFromQueue(m_fboID, destTexture,
323 m_sharedSurfaceTextureId,
324 m_sharedSurfaceTexture->getCurrentTextureTarget(),
325 index);
326 }
327
328 // After the base tile copied into the GL texture, we need to
329 // update the texture's info such that at draw time, readyFor
330 // will find the latest texture's info
331 // We don't need a map any more, each texture contains its own
332 // texturesTileInfo.
333 destTexture->setOwnTextureTileInfoFromQueue(&m_transferQueue[index].tileInfo);
334
335 XLOG("Blit tile x, y %d %d with dest texture %p to destTexture->m_ownTextureId %d",
336 m_transferQueue[index].tileInfo.m_x,
337 m_transferQueue[index].tileInfo.m_y,
338 destTexture,
339 destTexture->m_ownTextureId);
340 }
341 index = (index + 1) % ST_BUFFER_NUMBER;
342 }
343
344 // Clean up FBO setup. Doing this for both CPU/GPU upload can make the
345 // dynamic switch possible. Moving this out from the loop can save some
346 // milli-seconds.
347 if (usedFboForUpload) {
348 glBindFramebuffer(GL_FRAMEBUFFER, 0); // rebind the standard FBO
349 restoreGLState();
350 GLUtils::checkGlError("updateDirtyBaseTiles");
351 }
352
353 m_emptyItemCount = ST_BUFFER_NUMBER;
354 m_transferQueueItemCond.signal();
355 }
356
updateQueueWithBitmap(const TileRenderInfo * renderInfo,int x,int y,const SkBitmap & bitmap)357 void TransferQueue::updateQueueWithBitmap(const TileRenderInfo* renderInfo,
358 int x, int y, const SkBitmap& bitmap)
359 {
360 if (!tryUpdateQueueWithBitmap(renderInfo, x, y, bitmap)) {
361 // failed placing bitmap in queue, discard tile's texture so it will be
362 // re-enqueued (and repainted)
363 BaseTile* tile = renderInfo->baseTile;
364 if (tile)
365 tile->backTextureTransferFail();
366 }
367 }
368
tryUpdateQueueWithBitmap(const TileRenderInfo * renderInfo,int x,int y,const SkBitmap & bitmap)369 bool TransferQueue::tryUpdateQueueWithBitmap(const TileRenderInfo* renderInfo,
370 int x, int y, const SkBitmap& bitmap)
371 {
372 m_transferQueueItemLocks.lock();
373 bool ready = readyForUpdate();
374 TextureUploadType currentUploadType = m_currentUploadType;
375 m_transferQueueItemLocks.unlock();
376 if (!ready) {
377 XLOG("Quit bitmap update: not ready! for tile x y %d %d",
378 renderInfo->x, renderInfo->y);
379 return false;
380 }
381 if (currentUploadType == GpuUpload) {
382 // a) Dequeue the Surface Texture and write into the buffer
383 if (!m_ANW.get()) {
384 XLOG("ERROR: ANW is null");
385 return false;
386 }
387
388 ANativeWindow_Buffer buffer;
389 if (ANativeWindow_lock(m_ANW.get(), &buffer, 0))
390 return false;
391
392 uint8_t* img = (uint8_t*)buffer.bits;
393 int row, col;
394 int bpp = 4; // Now we only deal with RGBA8888 format.
395 int width = TilesManager::instance()->tileWidth();
396 int height = TilesManager::instance()->tileHeight();
397 if (!x && !y && bitmap.width() == width && bitmap.height() == height) {
398 bitmap.lockPixels();
399 uint8_t* bitmapOrigin = static_cast<uint8_t*>(bitmap.getPixels());
400 if (buffer.stride != bitmap.width())
401 // Copied line by line since we need to handle the offsets and stride.
402 for (row = 0 ; row < bitmap.height(); row ++) {
403 uint8_t* dst = &(img[buffer.stride * row * bpp]);
404 uint8_t* src = &(bitmapOrigin[bitmap.width() * row * bpp]);
405 memcpy(dst, src, bpp * bitmap.width());
406 }
407 else
408 memcpy(img, bitmapOrigin, bpp * bitmap.width() * bitmap.height());
409
410 bitmap.unlockPixels();
411 } else {
412 // TODO: implement the partial invalidate here!
413 XLOG("ERROR: don't expect to get here yet before we support partial inval");
414 }
415
416 ANativeWindow_unlockAndPost(m_ANW.get());
417 }
418
419 m_transferQueueItemLocks.lock();
420 // b) After update the Surface Texture, now udpate the transfer queue info.
421 addItemInTransferQueue(renderInfo, currentUploadType, &bitmap);
422
423 m_transferQueueItemLocks.unlock();
424 XLOG("Bitmap updated x, y %d %d, baseTile %p",
425 renderInfo->x, renderInfo->y, renderInfo->baseTile);
426 return true;
427 }
428
429 // Note that there should be lock/unlock around this function call.
430 // Currently only called by GLUtils::updateSharedSurfaceTextureWithBitmap.
addItemInTransferQueue(const TileRenderInfo * renderInfo,TextureUploadType type,const SkBitmap * bitmap)431 void TransferQueue::addItemInTransferQueue(const TileRenderInfo* renderInfo,
432 TextureUploadType type,
433 const SkBitmap* bitmap)
434 {
435 m_transferQueueIndex = (m_transferQueueIndex + 1) % ST_BUFFER_NUMBER;
436
437 int index = m_transferQueueIndex;
438 if (m_transferQueue[index].savedBaseTilePtr
439 || m_transferQueue[index].status != emptyItem) {
440 XLOG("ERROR update a tile which is dirty already @ index %d", index);
441 }
442
443 m_transferQueue[index].savedBaseTileTexturePtr = renderInfo->baseTile->backTexture();
444 m_transferQueue[index].savedBaseTilePtr = renderInfo->baseTile;
445 m_transferQueue[index].status = pendingBlit;
446 m_transferQueue[index].uploadType = type;
447 if (type == CpuUpload && bitmap) {
448 // Lazily create the bitmap
449 if (!m_transferQueue[index].bitmap) {
450 m_transferQueue[index].bitmap = new SkBitmap();
451 int w = bitmap->width();
452 int h = bitmap->height();
453 m_transferQueue[index].bitmap->setConfig(bitmap->config(), w, h);
454 }
455 bitmap->copyTo(m_transferQueue[index].bitmap, bitmap->config());
456 }
457
458 // Now fill the tileInfo.
459 TextureTileInfo* textureInfo = &m_transferQueue[index].tileInfo;
460
461 textureInfo->m_x = renderInfo->x;
462 textureInfo->m_y = renderInfo->y;
463 textureInfo->m_scale = renderInfo->scale;
464 textureInfo->m_painter = renderInfo->tilePainter;
465
466 textureInfo->m_picture = renderInfo->textureInfo->m_pictureCount;
467
468 m_emptyItemCount--;
469 }
470
setTextureUploadType(TextureUploadType type)471 void TransferQueue::setTextureUploadType(TextureUploadType type)
472 {
473 discardQueue();
474
475 android::Mutex::Autolock lock(m_transferQueueItemLocks);
476 m_currentUploadType = type;
477 XLOGC("Now we set the upload to %s", m_currentUploadType == GpuUpload ? "GpuUpload" : "CpuUpload");
478 }
479
480 // Note: this need to be called within th lock.
481 // Only called by updateDirtyBaseTiles() for now
cleanupTransportQueue()482 void TransferQueue::cleanupTransportQueue()
483 {
484 int index = getNextTransferQueueIndex();
485
486 for (int i = 0 ; i < ST_BUFFER_NUMBER; i++) {
487 if (m_transferQueue[index].status == pendingDiscard) {
488 // No matter what the current upload type is, as long as there has
489 // been a Surf Tex enqueue operation, this updateTexImage need to
490 // be called to keep things in sync.
491 if (m_transferQueue[index].uploadType == GpuUpload)
492 m_sharedSurfaceTexture->updateTexImage();
493
494 // since tiles in the queue may be from another webview, remove
495 // their textures so that they will be repainted / retransferred
496 BaseTile* tile = m_transferQueue[index].savedBaseTilePtr;
497 BaseTileTexture* texture = m_transferQueue[index].savedBaseTileTexturePtr;
498 if (tile && texture && texture->owner() == tile) {
499 // since tile destruction removes textures on the UI thread, the
500 // texture->owner ptr guarantees the tile is valid
501 tile->discardBackTexture();
502 XLOG("transfer queue discarded tile %p, removed texture", tile);
503 }
504
505 m_transferQueue[index].savedBaseTilePtr = 0;
506 m_transferQueue[index].savedBaseTileTexturePtr = 0;
507 m_transferQueue[index].status = emptyItem;
508 }
509 index = (index + 1) % ST_BUFFER_NUMBER;
510 }
511 }
512
saveGLState()513 void TransferQueue::saveGLState()
514 {
515 glGetIntegerv(GL_VIEWPORT, m_GLStateBeforeBlit.viewport);
516 glGetBooleanv(GL_SCISSOR_TEST, m_GLStateBeforeBlit.scissor);
517 glGetBooleanv(GL_DEPTH_TEST, m_GLStateBeforeBlit.depth);
518 #if DEBUG
519 glGetFloatv(GL_COLOR_CLEAR_VALUE, m_GLStateBeforeBlit.clearColor);
520 #endif
521 }
522
setGLStateForCopy(int width,int height)523 void TransferQueue::setGLStateForCopy(int width, int height)
524 {
525 // Need to match the texture size.
526 glViewport(0, 0, width, height);
527 glDisable(GL_SCISSOR_TEST);
528 glDisable(GL_DEPTH_TEST);
529 // Clear the content is only for debug purpose.
530 #if DEBUG
531 glClearColor(0, 0, 0, 0);
532 glClear(GL_COLOR_BUFFER_BIT);
533 #endif
534 }
535
restoreGLState()536 void TransferQueue::restoreGLState()
537 {
538 glViewport(m_GLStateBeforeBlit.viewport[0],
539 m_GLStateBeforeBlit.viewport[1],
540 m_GLStateBeforeBlit.viewport[2],
541 m_GLStateBeforeBlit.viewport[3]);
542
543 if (m_GLStateBeforeBlit.scissor[0])
544 glEnable(GL_SCISSOR_TEST);
545
546 if (m_GLStateBeforeBlit.depth[0])
547 glEnable(GL_DEPTH_TEST);
548 #if DEBUG
549 glClearColor(m_GLStateBeforeBlit.clearColor[0],
550 m_GLStateBeforeBlit.clearColor[1],
551 m_GLStateBeforeBlit.clearColor[2],
552 m_GLStateBeforeBlit.clearColor[3]);
553 #endif
554 }
555
getNextTransferQueueIndex()556 int TransferQueue::getNextTransferQueueIndex()
557 {
558 return (m_transferQueueIndex + 1) % ST_BUFFER_NUMBER;
559 }
560
561 } // namespace WebCore
562
563 #endif // USE(ACCELERATED_COMPOSITING
564