2 * Copyright 2011, The Android Open Source Project
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "TransferQueue.h"
29 #if USE(ACCELERATED_COMPOSITING)
32 #include "PaintedSurface.h"
33 #include <android/native_window.h>
34 #include <gui/SurfaceTexture.h>
35 #include <gui/SurfaceTextureClient.h>
37 #include <cutils/log.h>
38 #include <wtf/text/CString.h>
39 #define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "TransferQueue", __VA_ARGS__)
44 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "TransferQueue", __VA_ARGS__)
53 #define ST_BUFFER_NUMBER 4
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
61 TransferQueue::TransferQueue()
62 : m_eglSurface(EGL_NO_SURFACE)
63 , m_transferQueueIndex(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)
71 memset(&m_GLStateBeforeBlit, 0, sizeof(m_GLStateBeforeBlit));
73 m_emptyItemCount = ST_BUFFER_NUMBER;
75 m_transferQueue = new TileTransferData[ST_BUFFER_NUMBER];
78 TransferQueue::~TransferQueue()
80 glDeleteFramebuffers(1, &m_fboID);
82 glDeleteTextures(1, &m_sharedSurfaceTextureId);
83 m_sharedSurfaceTextureId = 0;
85 delete[] m_transferQueue;
88 void TransferQueue::initSharedSurfaceTextures(int width, int height)
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);
96 new android::SurfaceTexture(m_sharedSurfaceTextureId);
98 m_ANW = new android::SurfaceTextureClient(m_sharedSurfaceTexture);
99 m_sharedSurfaceTexture->setSynchronousMode(true);
100 m_sharedSurfaceTexture->setBufferCount(ST_BUFFER_NUMBER);
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);
111 glGenFramebuffers(1, &m_fboID);
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)
119 BaseTile* baseTilePtr = m_transferQueue[index].savedBaseTilePtr;
121 XLOG("Invalid savedBaseTilePtr , such that the tile is obsolete");
125 BaseTileTexture* baseTileTexture = baseTilePtr->backTexture();
126 if (!baseTileTexture) {
127 XLOG("Invalid baseTileTexture , such that the tile is obsolete");
131 const TextureTileInfo* tileInfo = &m_transferQueue[index].tileInfo;
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");
144 void TransferQueue::blitTileFromQueue(GLuint fboID, BaseTileTexture* destTex,
145 GLuint srcTexId, GLenum srcTexTarget,
148 #if GPU_UPLOAD_WITHOUT_DRAW
149 glBindFramebuffer(GL_FRAMEBUFFER, fboID);
150 glFramebufferTexture2D(GL_FRAMEBUFFER,
151 GL_COLOR_ATTACHMENT0,
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());
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,
165 destTex->m_ownTextureId,
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);
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);
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,
196 GLUtils::checkEglError("CreateSyncKHR");
200 void TransferQueue::interruptTransferQueue(bool interrupt)
202 m_transferQueueItemLocks.lock();
203 m_interruptedByRemovingOp = interrupt;
204 if (m_interruptedByRemovingOp)
205 m_transferQueueItemCond.signal();
206 m_transferQueueItemLocks.unlock();
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()
214 if (!getHasGLContext())
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)
221 m_transferQueueItemCond.wait(m_transferQueueItemLocks);
222 if (m_interruptedByRemovingOp)
226 if (!getHasGLContext())
229 // Disable this wait until we figure out why this didn't work on some
230 // drivers b/5332112.
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,
239 EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
242 GLUtils::checkEglError("WaitSyncKHR");
248 // Both getHasGLContext and setHasGLContext should be called within the lock.
249 bool TransferQueue::getHasGLContext()
251 return m_hasGLContext;
254 void TransferQueue::setHasGLContext(bool hasContext)
256 m_hasGLContext = hasContext;
259 // Only called when WebView is destroyed or switching the uploadType.
260 void TransferQueue::discardQueue()
262 android::Mutex::Autolock lock(m_transferQueueItemLocks);
264 for (int i = 0 ; i < ST_BUFFER_NUMBER; i++)
265 if (m_transferQueue[i].status == pendingBlit)
266 m_transferQueue[i].status = pendingDiscard;
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);
273 // Only signal once when GL context lost.
274 if (GLContextExisted)
275 m_transferQueueItemCond.signal();
278 // Call on UI thread to copy from the shared Surface Texture to the BaseTile's texture.
279 void TransferQueue::updateDirtyBaseTiles()
281 android::Mutex::Autolock lock(m_transferQueueItemLocks);
283 cleanupTransportQueue();
284 if (!getHasGLContext())
285 setHasGLContext(true);
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();
303 XLOGC("unexpected error: updateTexImage return %d", result);
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;
313 // guarantee that we have a texture to blit into
314 destTexture->requireGLTexture();
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);
321 if (!usedFboForUpload) {
323 usedFboForUpload = true;
325 blitTileFromQueue(m_fboID, destTexture,
326 m_sharedSurfaceTextureId,
327 m_sharedSurfaceTexture->getCurrentTextureTarget(),
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
336 destTexture->setOwnTextureTileInfoFromQueue(&m_transferQueue[index].tileInfo);
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,
342 destTexture->m_ownTextureId);
344 index = (index + 1) % ST_BUFFER_NUMBER;
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
350 if (usedFboForUpload) {
351 glBindFramebuffer(GL_FRAMEBUFFER, 0); // rebind the standard FBO
353 GLUtils::checkGlError("updateDirtyBaseTiles");
356 m_emptyItemCount = ST_BUFFER_NUMBER;
357 m_transferQueueItemCond.signal();
360 void TransferQueue::updateQueueWithBitmap(const TileRenderInfo* renderInfo,
361 int x, int y, const SkBitmap& bitmap)
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;
368 tile->backTextureTransferFail();
372 bool TransferQueue::tryUpdateQueueWithBitmap(const TileRenderInfo* renderInfo,
373 int x, int y, const SkBitmap& bitmap)
375 m_transferQueueItemLocks.lock();
376 bool ready = readyForUpdate();
377 TextureUploadType currentUploadType = m_currentUploadType;
378 m_transferQueueItemLocks.unlock();
380 XLOG("Quit bitmap update: not ready! for tile x y %d %d",
381 renderInfo->x, renderInfo->y);
384 if (currentUploadType == GpuUpload) {
385 // a) Dequeue the Surface Texture and write into the buffer
387 XLOG("ERROR: ANW is null");
391 ANativeWindow_Buffer buffer;
392 if (ANativeWindow_lock(m_ANW.get(), &buffer, 0))
395 uint8_t* img = (uint8_t*)buffer.bits;
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) {
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());
411 memcpy(img, bitmapOrigin, bpp * bitmap.width() * bitmap.height());
413 bitmap.unlockPixels();
415 // TODO: implement the partial invalidate here!
416 XLOG("ERROR: don't expect to get here yet before we support partial inval");
419 ANativeWindow_unlockAndPost(m_ANW.get());
422 m_transferQueueItemLocks.lock();
423 // b) After update the Surface Texture, now udpate the transfer queue info.
424 addItemInTransferQueue(renderInfo, currentUploadType, &bitmap);
426 m_transferQueueItemLocks.unlock();
427 XLOG("Bitmap updated x, y %d %d, baseTile %p",
428 renderInfo->x, renderInfo->y, renderInfo->baseTile);
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)
438 m_transferQueueIndex = (m_transferQueueIndex + 1) % ST_BUFFER_NUMBER;
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);
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);
458 bitmap->copyTo(m_transferQueue[index].bitmap, bitmap->config());
461 // Now fill the tileInfo.
462 TextureTileInfo* textureInfo = &m_transferQueue[index].tileInfo;
464 textureInfo->m_x = renderInfo->x;
465 textureInfo->m_y = renderInfo->y;
466 textureInfo->m_scale = renderInfo->scale;
467 textureInfo->m_painter = renderInfo->tilePainter;
469 textureInfo->m_picture = renderInfo->textureInfo->m_pictureCount;
474 void TransferQueue::setTextureUploadType(TextureUploadType type)
476 if (m_currentUploadType == type)
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");
486 // Note: this need to be called within th lock.
487 // Only called by updateDirtyBaseTiles() for now
488 void TransferQueue::cleanupTransportQueue()
490 int index = getNextTransferQueueIndex();
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();
500 XLOGC("unexpected error: updateTexImage return %d", result);
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);
514 m_transferQueue[index].savedBaseTilePtr = 0;
515 m_transferQueue[index].savedBaseTileTexturePtr = 0;
516 m_transferQueue[index].status = emptyItem;
518 index = (index + 1) % ST_BUFFER_NUMBER;
522 void TransferQueue::saveGLState()
524 glGetIntegerv(GL_VIEWPORT, m_GLStateBeforeBlit.viewport);
525 glGetBooleanv(GL_SCISSOR_TEST, m_GLStateBeforeBlit.scissor);
526 glGetBooleanv(GL_DEPTH_TEST, m_GLStateBeforeBlit.depth);
528 glGetFloatv(GL_COLOR_CLEAR_VALUE, m_GLStateBeforeBlit.clearColor);
532 void TransferQueue::setGLStateForCopy(int width, int height)
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.
540 glClearColor(0, 0, 0, 0);
541 glClear(GL_COLOR_BUFFER_BIT);
545 void TransferQueue::restoreGLState()
547 glViewport(m_GLStateBeforeBlit.viewport[0],
548 m_GLStateBeforeBlit.viewport[1],
549 m_GLStateBeforeBlit.viewport[2],
550 m_GLStateBeforeBlit.viewport[3]);
552 if (m_GLStateBeforeBlit.scissor[0])
553 glEnable(GL_SCISSOR_TEST);
555 if (m_GLStateBeforeBlit.depth[0])
556 glEnable(GL_DEPTH_TEST);
558 glClearColor(m_GLStateBeforeBlit.clearColor[0],
559 m_GLStateBeforeBlit.clearColor[1],
560 m_GLStateBeforeBlit.clearColor[2],
561 m_GLStateBeforeBlit.clearColor[3]);
565 int TransferQueue::getNextTransferQueueIndex()
567 return (m_transferQueueIndex + 1) % ST_BUFFER_NUMBER;
570 } // namespace WebCore
572 #endif // USE(ACCELERATED_COMPOSITING