OSDN Git Service

The new Gpu upload code path.
[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 0
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_currentDisplay(EGL_NO_DISPLAY)
68     , m_currentUploadType(DEFAULT_UPLOAD_TYPE)
69 {
70     memset(&m_GLStateBeforeBlit, 0, sizeof(m_GLStateBeforeBlit));
71
72     m_emptyItemCount = ST_BUFFER_NUMBER;
73
74     m_transferQueue = new TileTransferData[ST_BUFFER_NUMBER];
75 }
76
77 TransferQueue::~TransferQueue()
78 {
79     glDeleteFramebuffers(1, &m_fboID);
80     m_fboID = 0;
81     glDeleteTextures(1, &m_sharedSurfaceTextureId);
82     m_sharedSurfaceTextureId = 0;
83
84     delete[] m_transferQueue;
85 }
86
87 void TransferQueue::initSharedSurfaceTextures(int width, int height)
88 {
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);
94 #else
95             new android::SurfaceTexture(m_sharedSurfaceTextureId);
96 #endif
97         m_ANW = new android::SurfaceTextureClient(m_sharedSurfaceTexture);
98         m_sharedSurfaceTexture->setSynchronousMode(true);
99         m_sharedSurfaceTexture->setBufferCount(ST_BUFFER_NUMBER+1);
100
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);
107     }
108
109     if (!m_fboID)
110         glGenFramebuffers(1, &m_fboID);
111 }
112
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)
117 {
118     BaseTile* baseTilePtr = m_transferQueue[index].savedBaseTilePtr;
119     if (!baseTilePtr) {
120         XLOG("Invalid savedBaseTilePtr , such that the tile is obsolete");
121         return true;
122     }
123
124     BaseTileTexture* baseTileTexture = baseTilePtr->backTexture();
125     if (!baseTileTexture) {
126         XLOG("Invalid baseTileTexture , such that the tile is obsolete");
127         return true;
128     }
129
130     const TextureTileInfo* tileInfo = &m_transferQueue[index].tileInfo;
131
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");
137         return true;
138     }
139
140     return false;
141 }
142
143 void TransferQueue::blitTileFromQueue(GLuint fboID, BaseTileTexture* destTex,
144                                       GLuint srcTexId, GLenum srcTexTarget,
145                                       int index)
146 {
147 #if GPU_UPLOAD_WITHOUT_DRAW
148     glBindFramebuffer(GL_FRAMEBUFFER, fboID);
149     glFramebufferTexture2D(GL_FRAMEBUFFER,
150                            GL_COLOR_ATTACHMENT0,
151                            GL_TEXTURE_2D,
152                            srcTexId,
153                            0);
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());
158 #else
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,
163                            GL_TEXTURE_2D,
164                            destTex->m_ownTextureId,
165                            0);
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);
172         return;
173     }
174
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);
179
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,
192                                                             EGL_SYNC_FENCE_KHR,
193                                                             0);
194     }
195     GLUtils::checkEglError("CreateSyncKHR");
196 #endif
197 }
198
199 void TransferQueue::interruptTransferQueue(bool interrupt)
200 {
201     m_transferQueueItemLocks.lock();
202     m_interruptedByRemovingOp = interrupt;
203     if (m_interruptedByRemovingOp)
204         m_transferQueueItemCond.signal();
205     m_transferQueueItemLocks.unlock();
206 }
207
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()
212 {
213     if (!getHasGLContext())
214         return false;
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)
219             return false;
220         m_transferQueueItemCond.wait(m_transferQueueItemLocks);
221         if (m_interruptedByRemovingOp)
222             return false;
223     }
224
225     if (!getHasGLContext())
226         return false;
227
228     // Disable this wait until we figure out why this didn't work on some
229     // drivers b/5332112.
230 #if 0
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,
237                                  syncKHR,
238                                  EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
239                                  EGL_FOREVER_KHR);
240     }
241     GLUtils::checkEglError("WaitSyncKHR");
242 #endif
243
244     return true;
245 }
246
247 // Both getHasGLContext and setHasGLContext should be called within the lock.
248 bool TransferQueue::getHasGLContext()
249 {
250     return m_hasGLContext;
251 }
252
253 void TransferQueue::setHasGLContext(bool hasContext)
254 {
255     m_hasGLContext = hasContext;
256 }
257
258 // Only called when WebView is destroyed or switching the uploadType.
259 void TransferQueue::discardQueue()
260 {
261     android::Mutex::Autolock lock(m_transferQueueItemLocks);
262
263     for (int i = 0 ; i < ST_BUFFER_NUMBER; i++)
264         if (m_transferQueue[i].status == pendingBlit)
265             m_transferQueue[i].status = pendingDiscard;
266
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);
271
272     // Only signal once when GL context lost.
273     if (GLContextExisted)
274         m_transferQueueItemCond.signal();
275 }
276
277 // Call on UI thread to copy from the shared Surface Texture to the BaseTile's texture.
278 void TransferQueue::updateDirtyBaseTiles()
279 {
280     android::Mutex::Autolock lock(m_transferQueueItemLocks);
281
282     cleanupTransportQueue();
283     if (!getHasGLContext())
284         setHasGLContext(true);
285
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;
306                 continue;
307             }
308
309             // guarantee that we have a texture to blit into
310             destTexture->requireTexture();
311
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);
316             } else {
317                 if (!usedFboForUpload) {
318                     saveGLState();
319                     usedFboForUpload = true;
320                 }
321                 blitTileFromQueue(m_fboID, destTexture,
322                                   m_sharedSurfaceTextureId,
323                                   m_sharedSurfaceTexture->getCurrentTextureTarget(),
324                                   index);
325             }
326
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
331             // texturesTileInfo.
332             destTexture->setOwnTextureTileInfoFromQueue(&m_transferQueue[index].tileInfo);
333
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,
337                  destTexture,
338                  destTexture->m_ownTextureId);
339         }
340         index = (index + 1) % ST_BUFFER_NUMBER;
341     }
342
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
345     // milli-seconds.
346     if (usedFboForUpload) {
347         glBindFramebuffer(GL_FRAMEBUFFER, 0); // rebind the standard FBO
348         restoreGLState();
349         GLUtils::checkGlError("updateDirtyBaseTiles");
350     }
351
352     m_emptyItemCount = ST_BUFFER_NUMBER;
353     m_transferQueueItemCond.signal();
354 }
355
356 void TransferQueue::updateQueueWithBitmap(const TileRenderInfo* renderInfo,
357                                           int x, int y, const SkBitmap& bitmap)
358 {
359     m_transferQueueItemLocks.lock();
360     bool ready = readyForUpdate();
361     TextureUploadType currentUploadType = m_currentUploadType;
362     m_transferQueueItemLocks.unlock();
363     if (!ready) {
364         XLOG("Quit bitmap update: not ready! for tile x y %d %d",
365              renderInfo->x, renderInfo->y);
366         return;
367     }
368     if (currentUploadType == GpuUpload) {
369         // a) Dequeue the Surface Texture and write into the buffer
370         if (!m_ANW.get()) {
371             XLOG("ERROR: ANW is null");
372             return;
373         }
374
375         ANativeWindow_Buffer buffer;
376         if (ANativeWindow_lock(m_ANW.get(), &buffer, 0))
377             return;
378
379         uint8_t* img = (uint8_t*)buffer.bits;
380         int row, col;
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) {
385             bitmap.lockPixels();
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());
393                 }
394             else
395                 memcpy(img, bitmapOrigin, bpp * bitmap.width() * bitmap.height());
396
397             bitmap.unlockPixels();
398         } else {
399             // TODO: implement the partial invalidate here!
400             XLOG("ERROR: don't expect to get here yet before we support partial inval");
401         }
402
403         ANativeWindow_unlockAndPost(m_ANW.get());
404     }
405
406     m_transferQueueItemLocks.lock();
407     // b) After update the Surface Texture, now udpate the transfer queue info.
408     addItemInTransferQueue(renderInfo, currentUploadType, &bitmap);
409
410     m_transferQueueItemLocks.unlock();
411     XLOG("Bitmap updated x, y %d %d, baseTile %p",
412          renderInfo->x, renderInfo->y, renderInfo->baseTile);
413 }
414
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)
420 {
421     m_transferQueueIndex = (m_transferQueueIndex + 1) % ST_BUFFER_NUMBER;
422
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);
427     }
428
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);
439         }
440         bitmap->copyTo(m_transferQueue[index].bitmap, bitmap->config());
441     }
442
443     // Now fill the tileInfo.
444     TextureTileInfo* textureInfo = &m_transferQueue[index].tileInfo;
445
446     textureInfo->m_x = renderInfo->x;
447     textureInfo->m_y = renderInfo->y;
448     textureInfo->m_scale = renderInfo->scale;
449     textureInfo->m_painter = renderInfo->tilePainter;
450
451     textureInfo->m_picture = renderInfo->textureInfo->m_pictureCount;
452
453     m_emptyItemCount--;
454 }
455
456 void TransferQueue::setTextureUploadType(TextureUploadType type)
457 {
458     discardQueue();
459
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");
463 }
464
465 // Note: this need to be called within th lock.
466 // Only called by updateDirtyBaseTiles() for now
467 void TransferQueue::cleanupTransportQueue()
468 {
469     int index = getNextTransferQueueIndex();
470
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;
480         }
481         index = (index + 1) % ST_BUFFER_NUMBER;
482     }
483 }
484
485 void TransferQueue::saveGLState()
486 {
487     glGetIntegerv(GL_VIEWPORT, m_GLStateBeforeBlit.viewport);
488     glGetBooleanv(GL_SCISSOR_TEST, m_GLStateBeforeBlit.scissor);
489     glGetBooleanv(GL_DEPTH_TEST, m_GLStateBeforeBlit.depth);
490 #if DEBUG
491     glGetFloatv(GL_COLOR_CLEAR_VALUE, m_GLStateBeforeBlit.clearColor);
492 #endif
493 }
494
495 void TransferQueue::setGLStateForCopy(int width, int height)
496 {
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.
502 #if DEBUG
503     glClearColor(0, 0, 0, 0);
504     glClear(GL_COLOR_BUFFER_BIT);
505 #endif
506 }
507
508 void TransferQueue::restoreGLState()
509 {
510     glViewport(m_GLStateBeforeBlit.viewport[0],
511                m_GLStateBeforeBlit.viewport[1],
512                m_GLStateBeforeBlit.viewport[2],
513                m_GLStateBeforeBlit.viewport[3]);
514
515     if (m_GLStateBeforeBlit.scissor[0])
516         glEnable(GL_SCISSOR_TEST);
517
518     if (m_GLStateBeforeBlit.depth[0])
519         glEnable(GL_DEPTH_TEST);
520 #if DEBUG
521     glClearColor(m_GLStateBeforeBlit.clearColor[0],
522                  m_GLStateBeforeBlit.clearColor[1],
523                  m_GLStateBeforeBlit.clearColor[2],
524                  m_GLStateBeforeBlit.clearColor[3]);
525 #endif
526 }
527
528 int TransferQueue::getNextTransferQueueIndex()
529 {
530     return (m_transferQueueIndex + 1) % ST_BUFFER_NUMBER;
531 }
532
533 } // namespace WebCore
534
535 #endif // USE(ACCELERATED_COMPOSITING