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 0
61 TransferQueue::TransferQueue()
62 : m_eglSurface(EGL_NO_SURFACE)
63 , m_transferQueueIndex(0)
65 , m_sharedSurfaceTextureId(0)
66 , m_hasGLContext(true)
67 , m_currentDisplay(EGL_NO_DISPLAY)
68 , m_currentUploadType(DEFAULT_UPLOAD_TYPE)
70 memset(&m_GLStateBeforeBlit, 0, sizeof(m_GLStateBeforeBlit));
72 m_emptyItemCount = ST_BUFFER_NUMBER;
74 m_transferQueue = new TileTransferData[ST_BUFFER_NUMBER];
77 TransferQueue::~TransferQueue()
79 glDeleteFramebuffers(1, &m_fboID);
81 glDeleteTextures(1, &m_sharedSurfaceTextureId);
82 m_sharedSurfaceTextureId = 0;
84 delete[] m_transferQueue;
87 void TransferQueue::initSharedSurfaceTextures(int width, int height)
89 if (!m_sharedSurfaceTextureId) {
90 glGenTextures(1, &m_sharedSurfaceTextureId);
91 m_sharedSurfaceTexture =
92 #if GPU_UPLOAD_WITHOUT_DRAW
93 new android::SurfaceTexture(m_sharedSurfaceTextureId, true, GL_TEXTURE_2D);
95 new android::SurfaceTexture(m_sharedSurfaceTextureId);
97 m_ANW = new android::SurfaceTextureClient(m_sharedSurfaceTexture);
98 m_sharedSurfaceTexture->setSynchronousMode(true);
99 m_sharedSurfaceTexture->setBufferCount(ST_BUFFER_NUMBER+1);
101 int result = native_window_set_buffers_geometry(m_ANW.get(),
102 width, height, HAL_PIXEL_FORMAT_RGBA_8888);
103 GLUtils::checkSurfaceTextureError("native_window_set_buffers_geometry", result);
104 result = native_window_set_usage(m_ANW.get(),
105 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
106 GLUtils::checkSurfaceTextureError("native_window_set_usage", result);
110 glGenFramebuffers(1, &m_fboID);
113 // When bliting, if the item from the transfer queue is mismatching b/t the
114 // BaseTile and the content, then the item is considered as obsolete, and
115 // the content is discarded.
116 bool TransferQueue::checkObsolete(int index)
118 BaseTile* baseTilePtr = m_transferQueue[index].savedBaseTilePtr;
120 XLOG("Invalid savedBaseTilePtr , such that the tile is obsolete");
124 BaseTileTexture* baseTileTexture = baseTilePtr->backTexture();
125 if (!baseTileTexture) {
126 XLOG("Invalid baseTileTexture , such that the tile is obsolete");
130 const TextureTileInfo* tileInfo = &m_transferQueue[index].tileInfo;
132 if (tileInfo->m_x != baseTilePtr->x()
133 || tileInfo->m_y != baseTilePtr->y()
134 || tileInfo->m_scale != baseTilePtr->scale()
135 || tileInfo->m_painter != baseTilePtr->painter()) {
136 XLOG("Mismatching x, y, scale or painter , such that the tile is obsolete");
143 void TransferQueue::blitTileFromQueue(GLuint fboID, BaseTileTexture* destTex,
144 GLuint srcTexId, GLenum srcTexTarget,
147 #if GPU_UPLOAD_WITHOUT_DRAW
148 glBindFramebuffer(GL_FRAMEBUFFER, fboID);
149 glFramebufferTexture2D(GL_FRAMEBUFFER,
150 GL_COLOR_ATTACHMENT0,
154 glBindTexture(GL_TEXTURE_2D, destTex->m_ownTextureId);
155 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0,
156 destTex->getSize().width(),
157 destTex->getSize().height());
159 // Then set up the FBO and copy the SurfTex content in.
160 glBindFramebuffer(GL_FRAMEBUFFER, fboID);
161 glFramebufferTexture2D(GL_FRAMEBUFFER,
162 GL_COLOR_ATTACHMENT0,
164 destTex->m_ownTextureId,
166 setGLStateForCopy(destTex->getSize().width(),
167 destTex->getSize().height());
168 GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
169 if (status != GL_FRAMEBUFFER_COMPLETE) {
170 XLOG("Error: glCheckFramebufferStatus failed");
171 glBindFramebuffer(GL_FRAMEBUFFER, 0);
175 // Use empty rect to set up the special matrix to draw.
176 SkRect rect = SkRect::MakeEmpty();
177 TilesManager::instance()->shader()->drawQuad(rect, srcTexId, 1.0,
178 srcTexTarget, GL_NEAREST);
180 // To workaround a sync issue on some platforms, we should insert the sync
181 // here while in the current FBO.
182 // This will essentially kick off the GPU command buffer, and the Tex Gen
183 // thread will then have to wait for this buffer to finish before writing
184 // into the same memory.
185 EGLDisplay dpy = eglGetCurrentDisplay();
186 if (m_currentDisplay != dpy)
187 m_currentDisplay = dpy;
188 if (m_currentDisplay != EGL_NO_DISPLAY) {
189 if (m_transferQueue[index].m_syncKHR != EGL_NO_SYNC_KHR)
190 eglDestroySyncKHR(m_currentDisplay, m_transferQueue[index].m_syncKHR);
191 m_transferQueue[index].m_syncKHR = eglCreateSyncKHR(m_currentDisplay,
195 GLUtils::checkEglError("CreateSyncKHR");
199 void TransferQueue::interruptTransferQueue(bool interrupt)
201 m_transferQueueItemLocks.lock();
202 m_interruptedByRemovingOp = interrupt;
203 if (m_interruptedByRemovingOp)
204 m_transferQueueItemCond.signal();
205 m_transferQueueItemLocks.unlock();
208 // This function must be called inside the m_transferQueueItemLocks, for the
209 // wait, m_interruptedByRemovingOp and getHasGLContext().
210 // Only called by updateQueueWithBitmap() for now.
211 bool TransferQueue::readyForUpdate()
213 if (!getHasGLContext())
215 // Don't use a while loop since when the WebView tear down, the emptyCount
216 // will still be 0, and we bailed out b/c of GL context lost.
217 if (!m_emptyItemCount) {
218 if (m_interruptedByRemovingOp)
220 m_transferQueueItemCond.wait(m_transferQueueItemLocks);
221 if (m_interruptedByRemovingOp)
225 if (!getHasGLContext())
228 // Disable this wait until we figure out why this didn't work on some
229 // drivers b/5332112.
231 if (m_currentUploadType == GpuUpload
232 && m_currentDisplay != EGL_NO_DISPLAY) {
233 // Check the GPU fence
234 EGLSyncKHR syncKHR = m_transferQueue[getNextTransferQueueIndex()].m_syncKHR;
235 if (syncKHR != EGL_NO_SYNC_KHR)
236 eglClientWaitSyncKHR(m_currentDisplay,
238 EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
241 GLUtils::checkEglError("WaitSyncKHR");
247 // Both getHasGLContext and setHasGLContext should be called within the lock.
248 bool TransferQueue::getHasGLContext()
250 return m_hasGLContext;
253 void TransferQueue::setHasGLContext(bool hasContext)
255 m_hasGLContext = hasContext;
258 // Only called when WebView is destroyed or switching the uploadType.
259 void TransferQueue::discardQueue()
261 android::Mutex::Autolock lock(m_transferQueueItemLocks);
263 for (int i = 0 ; i < ST_BUFFER_NUMBER; i++)
264 if (m_transferQueue[i].status == pendingBlit)
265 m_transferQueue[i].status = pendingDiscard;
267 bool GLContextExisted = getHasGLContext();
268 // Unblock the Tex Gen thread first before Tile Page deletion.
269 // Otherwise, there will be a deadlock while removing operations.
270 setHasGLContext(false);
272 // Only signal once when GL context lost.
273 if (GLContextExisted)
274 m_transferQueueItemCond.signal();
277 // Call on UI thread to copy from the shared Surface Texture to the BaseTile's texture.
278 void TransferQueue::updateDirtyBaseTiles()
280 android::Mutex::Autolock lock(m_transferQueueItemLocks);
282 cleanupTransportQueue();
283 if (!getHasGLContext())
284 setHasGLContext(true);
286 // Start from the oldest item, we call the updateTexImage to retrive
287 // the texture and blit that into each BaseTile's texture.
288 const int nextItemIndex = getNextTransferQueueIndex();
289 int index = nextItemIndex;
290 bool usedFboForUpload = false;
291 for (int k = 0; k < ST_BUFFER_NUMBER ; k++) {
292 if (m_transferQueue[index].status == pendingBlit) {
293 bool obsoleteBaseTile = checkObsolete(index);
294 // Save the needed info, update the Surf Tex, clean up the item in
295 // the queue. Then either move on to next item or copy the content.
296 BaseTileTexture* destTexture = 0;
297 if (!obsoleteBaseTile)
298 destTexture = m_transferQueue[index].savedBaseTilePtr->backTexture();
299 if (m_transferQueue[index].uploadType == GpuUpload)
300 m_sharedSurfaceTexture->updateTexImage();
301 m_transferQueue[index].savedBaseTilePtr = 0;
302 m_transferQueue[index].status = emptyItem;
303 if (obsoleteBaseTile) {
304 XLOG("Warning: the texture is obsolete for this baseTile");
305 index = (index + 1) % ST_BUFFER_NUMBER;
309 // guarantee that we have a texture to blit into
310 destTexture->requireTexture();
312 if (m_transferQueue[index].uploadType == CpuUpload) {
313 // Here we just need to upload the bitmap content to the GL Texture
314 GLUtils::updateTextureWithBitmap(destTexture->m_ownTextureId, 0, 0,
315 *m_transferQueue[index].bitmap);
317 if (!usedFboForUpload) {
319 usedFboForUpload = true;
321 blitTileFromQueue(m_fboID, destTexture,
322 m_sharedSurfaceTextureId,
323 m_sharedSurfaceTexture->getCurrentTextureTarget(),
327 // After the base tile copied into the GL texture, we need to
328 // update the texture's info such that at draw time, readyFor
329 // will find the latest texture's info
330 // We don't need a map any more, each texture contains its own
332 destTexture->setOwnTextureTileInfoFromQueue(&m_transferQueue[index].tileInfo);
334 XLOG("Blit tile x, y %d %d with dest texture %p to destTexture->m_ownTextureId %d",
335 m_transferQueue[index].tileInfo.m_x,
336 m_transferQueue[index].tileInfo.m_y,
338 destTexture->m_ownTextureId);
340 index = (index + 1) % ST_BUFFER_NUMBER;
343 // Clean up FBO setup. Doing this for both CPU/GPU upload can make the
344 // dynamic switch possible. Moving this out from the loop can save some
346 if (usedFboForUpload) {
347 glBindFramebuffer(GL_FRAMEBUFFER, 0); // rebind the standard FBO
349 GLUtils::checkGlError("updateDirtyBaseTiles");
352 m_emptyItemCount = ST_BUFFER_NUMBER;
353 m_transferQueueItemCond.signal();
356 void TransferQueue::updateQueueWithBitmap(const TileRenderInfo* renderInfo,
357 int x, int y, const SkBitmap& bitmap)
359 m_transferQueueItemLocks.lock();
360 bool ready = readyForUpdate();
361 TextureUploadType currentUploadType = m_currentUploadType;
362 m_transferQueueItemLocks.unlock();
364 XLOG("Quit bitmap update: not ready! for tile x y %d %d",
365 renderInfo->x, renderInfo->y);
368 if (currentUploadType == GpuUpload) {
369 // a) Dequeue the Surface Texture and write into the buffer
371 XLOG("ERROR: ANW is null");
375 ANativeWindow_Buffer buffer;
376 if (ANativeWindow_lock(m_ANW.get(), &buffer, 0))
379 uint8_t* img = (uint8_t*)buffer.bits;
381 int bpp = 4; // Now we only deal with RGBA8888 format.
382 int width = TilesManager::instance()->tileWidth();
383 int height = TilesManager::instance()->tileHeight();
384 if (!x && !y && bitmap.width() == width && bitmap.height() == height) {
386 uint8_t* bitmapOrigin = static_cast<uint8_t*>(bitmap.getPixels());
387 if (buffer.stride != bitmap.width())
388 // Copied line by line since we need to handle the offsets and stride.
389 for (row = 0 ; row < bitmap.height(); row ++) {
390 uint8_t* dst = &(img[buffer.stride * row * bpp]);
391 uint8_t* src = &(bitmapOrigin[bitmap.width() * row * bpp]);
392 memcpy(dst, src, bpp * bitmap.width());
395 memcpy(img, bitmapOrigin, bpp * bitmap.width() * bitmap.height());
397 bitmap.unlockPixels();
399 // TODO: implement the partial invalidate here!
400 XLOG("ERROR: don't expect to get here yet before we support partial inval");
403 ANativeWindow_unlockAndPost(m_ANW.get());
406 m_transferQueueItemLocks.lock();
407 // b) After update the Surface Texture, now udpate the transfer queue info.
408 addItemInTransferQueue(renderInfo, currentUploadType, &bitmap);
410 m_transferQueueItemLocks.unlock();
411 XLOG("Bitmap updated x, y %d %d, baseTile %p",
412 renderInfo->x, renderInfo->y, renderInfo->baseTile);
415 // Note that there should be lock/unlock around this function call.
416 // Currently only called by GLUtils::updateSharedSurfaceTextureWithBitmap.
417 void TransferQueue::addItemInTransferQueue(const TileRenderInfo* renderInfo,
418 TextureUploadType type,
419 const SkBitmap* bitmap)
421 m_transferQueueIndex = (m_transferQueueIndex + 1) % ST_BUFFER_NUMBER;
423 int index = m_transferQueueIndex;
424 if (m_transferQueue[index].savedBaseTilePtr
425 || m_transferQueue[index].status != emptyItem) {
426 XLOG("ERROR update a tile which is dirty already @ index %d", index);
429 m_transferQueue[index].savedBaseTilePtr = renderInfo->baseTile;
430 m_transferQueue[index].status = pendingBlit;
431 m_transferQueue[index].uploadType = type;
432 if (type == CpuUpload && bitmap) {
433 // Lazily create the bitmap
434 if (!m_transferQueue[index].bitmap) {
435 m_transferQueue[index].bitmap = new SkBitmap();
436 int w = bitmap->width();
437 int h = bitmap->height();
438 m_transferQueue[index].bitmap->setConfig(bitmap->config(), w, h);
440 bitmap->copyTo(m_transferQueue[index].bitmap, bitmap->config());
443 // Now fill the tileInfo.
444 TextureTileInfo* textureInfo = &m_transferQueue[index].tileInfo;
446 textureInfo->m_x = renderInfo->x;
447 textureInfo->m_y = renderInfo->y;
448 textureInfo->m_scale = renderInfo->scale;
449 textureInfo->m_painter = renderInfo->tilePainter;
451 textureInfo->m_picture = renderInfo->textureInfo->m_pictureCount;
456 void TransferQueue::setTextureUploadType(TextureUploadType type)
460 android::Mutex::Autolock lock(m_transferQueueItemLocks);
461 m_currentUploadType = type;
462 XLOGC("Now we set the upload to %s", m_currentUploadType == GpuUpload ? "GpuUpload" : "CpuUpload");
465 // Note: this need to be called within th lock.
466 // Only called by updateDirtyBaseTiles() for now
467 void TransferQueue::cleanupTransportQueue()
469 int index = getNextTransferQueueIndex();
471 for (int i = 0 ; i < ST_BUFFER_NUMBER; i++) {
472 if (m_transferQueue[index].status == pendingDiscard) {
473 // No matter what the current upload type is, as long as there has
474 // been a Surf Tex enqueue operation, this updateTexImage need to
475 // be called to keep things in sync.
476 if (m_transferQueue[index].uploadType == GpuUpload)
477 m_sharedSurfaceTexture->updateTexImage();
478 m_transferQueue[index].savedBaseTilePtr = 0;
479 m_transferQueue[index].status = emptyItem;
481 index = (index + 1) % ST_BUFFER_NUMBER;
485 void TransferQueue::saveGLState()
487 glGetIntegerv(GL_VIEWPORT, m_GLStateBeforeBlit.viewport);
488 glGetBooleanv(GL_SCISSOR_TEST, m_GLStateBeforeBlit.scissor);
489 glGetBooleanv(GL_DEPTH_TEST, m_GLStateBeforeBlit.depth);
491 glGetFloatv(GL_COLOR_CLEAR_VALUE, m_GLStateBeforeBlit.clearColor);
495 void TransferQueue::setGLStateForCopy(int width, int height)
497 // Need to match the texture size.
498 glViewport(0, 0, width, height);
499 glDisable(GL_SCISSOR_TEST);
500 glDisable(GL_DEPTH_TEST);
501 // Clear the content is only for debug purpose.
503 glClearColor(0, 0, 0, 0);
504 glClear(GL_COLOR_BUFFER_BIT);
508 void TransferQueue::restoreGLState()
510 glViewport(m_GLStateBeforeBlit.viewport[0],
511 m_GLStateBeforeBlit.viewport[1],
512 m_GLStateBeforeBlit.viewport[2],
513 m_GLStateBeforeBlit.viewport[3]);
515 if (m_GLStateBeforeBlit.scissor[0])
516 glEnable(GL_SCISSOR_TEST);
518 if (m_GLStateBeforeBlit.depth[0])
519 glEnable(GL_DEPTH_TEST);
521 glClearColor(m_GLStateBeforeBlit.clearColor[0],
522 m_GLStateBeforeBlit.clearColor[1],
523 m_GLStateBeforeBlit.clearColor[2],
524 m_GLStateBeforeBlit.clearColor[3]);
528 int TransferQueue::getNextTransferQueueIndex()
530 return (m_transferQueueIndex + 1) % ST_BUFFER_NUMBER;
533 } // namespace WebCore
535 #endif // USE(ACCELERATED_COMPOSITING