From 2ac76fe4e18c3325c8b3bb8f9435fdc7b96c0aca Mon Sep 17 00:00:00 2001 From: Teng-Hui Zhu Date: Thu, 19 Apr 2012 14:54:31 -0700 Subject: Better handle the EGL context lost situation. Unless framework provide a better message, we can't avoid this EGL context issue totally if mis-match happen again. Clean up some obsolete code. Change-Id: Ica03daecd58f9757c8cad41e0f40d5d51b041748 --- .../platform/graphics/android/GLWebViewState.cpp | 8 +-- .../graphics/android/layers/VideoLayerManager.cpp | 19 ++++--- .../graphics/android/layers/VideoLayerManager.h | 1 + .../graphics/android/rendering/GLUtils.cpp | 43 ---------------- .../platform/graphics/android/rendering/GLUtils.h | 1 - .../graphics/android/rendering/ShaderProgram.h | 2 +- .../graphics/android/rendering/TilesManager.cpp | 58 ++++++++++++++++++++++ .../graphics/android/rendering/TilesManager.h | 7 +++ .../graphics/android/rendering/TransferQueue.cpp | 35 +++++++++---- .../graphics/android/rendering/TransferQueue.h | 7 +-- 10 files changed, 106 insertions(+), 75 deletions(-) (limited to 'Source/WebCore') diff --git a/Source/WebCore/platform/graphics/android/GLWebViewState.cpp b/Source/WebCore/platform/graphics/android/GLWebViewState.cpp index 8581a8e..a52a3fe 100644 --- a/Source/WebCore/platform/graphics/android/GLWebViewState.cpp +++ b/Source/WebCore/platform/graphics/android/GLWebViewState.cpp @@ -323,10 +323,7 @@ int GLWebViewState::drawGL(IntRect& rect, SkRect& viewport, IntRect* invalRect, if (scale < MIN_SCALE_WARNING || scale > MAX_SCALE_WARNING) ALOGW("WARNING, scale seems corrupted before update: %e", scale); - // Here before we draw, update the Tile which has updated content. - // Inside this function, just do GPU blits from the transfer queue into - // the Tiles' texture. - tilesManager->transferQueue()->updateDirtyTiles(); + tilesManager->updateTilesIfContextVerified(); // Upload any pending ImageTexture // Return true if we still have some images to upload. @@ -366,9 +363,6 @@ int GLWebViewState::drawGL(IntRect& rect, SkRect& viewport, IntRect* invalRect, glBindBuffer(GL_ARRAY_BUFFER, 0); - // Clean up GL textures for video layer. - tilesManager->videoLayerManager()->deleteUnusedTextures(); - if (returnFlags & uirenderer::DrawGlInfo::kStatusDraw) { // returnFlags & kStatusDraw && empty inval region means we've inval'd everything, // but don't have new content. Keep redrawing full view (0,0,0,0) diff --git a/Source/WebCore/platform/graphics/android/layers/VideoLayerManager.cpp b/Source/WebCore/platform/graphics/android/layers/VideoLayerManager.cpp index 6501f98..a7b3184 100644 --- a/Source/WebCore/platform/graphics/android/layers/VideoLayerManager.cpp +++ b/Source/WebCore/platform/graphics/android/layers/VideoLayerManager.cpp @@ -107,16 +107,15 @@ void VideoLayerManager::initGLResourcesIfNeeded() void VideoLayerManager::initGLResources() { GLUtils::checkGlError("before initGLResources()"); - if (!m_createdTexture) { - m_spinnerOuterTextureId = - createTextureFromImage(RenderSkinMediaButton::SPINNER_OUTER); - m_spinnerInnerTextureId = - createTextureFromImage(RenderSkinMediaButton::SPINNER_INNER); - m_posterTextureId = - createTextureFromImage(RenderSkinMediaButton::VIDEO); - m_playTextureId = createTextureFromImage(RenderSkinMediaButton::PLAY); - m_pauseTextureId = createTextureFromImage(RenderSkinMediaButton::PAUSE); - } + m_spinnerOuterTextureId = + createTextureFromImage(RenderSkinMediaButton::SPINNER_OUTER); + m_spinnerInnerTextureId = + createTextureFromImage(RenderSkinMediaButton::SPINNER_INNER); + m_posterTextureId = + createTextureFromImage(RenderSkinMediaButton::VIDEO); + m_playTextureId = createTextureFromImage(RenderSkinMediaButton::PLAY); + m_pauseTextureId = createTextureFromImage(RenderSkinMediaButton::PAUSE); + m_createdTexture = !GLUtils::checkGlError("initGLResources()"); return; } diff --git a/Source/WebCore/platform/graphics/android/layers/VideoLayerManager.h b/Source/WebCore/platform/graphics/android/layers/VideoLayerManager.h index 6c02534..adce0f4 100644 --- a/Source/WebCore/platform/graphics/android/layers/VideoLayerManager.h +++ b/Source/WebCore/platform/graphics/android/layers/VideoLayerManager.h @@ -97,6 +97,7 @@ public: void initGLResourcesIfNeeded(); void cleanupGLResources(); + void forceNeedsInit() { m_createdTexture = false; } static int getButtonSize(); private: diff --git a/Source/WebCore/platform/graphics/android/rendering/GLUtils.cpp b/Source/WebCore/platform/graphics/android/rendering/GLUtils.cpp index 9435065..7206c98 100644 --- a/Source/WebCore/platform/graphics/android/rendering/GLUtils.cpp +++ b/Source/WebCore/platform/graphics/android/rendering/GLUtils.cpp @@ -298,49 +298,6 @@ static EGLSurface createPbufferSurface(EGLDisplay display, const EGLConfig& conf return surface; } -EGLContext GLUtils::createBackgroundContext(EGLContext sharedContext) -{ - checkEglError(""); - EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - checkEglError("eglGetDisplay"); - if (display == EGL_NO_DISPLAY) { - ALOGE("eglGetDisplay returned EGL_NO_DISPLAY"); - return EGL_NO_CONTEXT; - } - - EGLint majorVersion; - EGLint minorVersion; - EGLBoolean returnValue = eglInitialize(display, &majorVersion, &minorVersion); - checkEglError("eglInitialize", returnValue); - if (returnValue != EGL_TRUE) { - ALOGE("eglInitialize failed\n"); - return EGL_NO_CONTEXT; - } - - EGLConfig config = defaultPbufferConfig(display); - EGLSurface surface = createPbufferSurface(display, config, 0); - - EGLint surfaceConfigId; - EGLBoolean success = eglGetConfigAttrib(display, config, EGL_CONFIG_ID, &surfaceConfigId); - - EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; - EGLContext context = eglCreateContext(display, config, sharedContext, contextAttribs); - checkEglError("eglCreateContext"); - if (context == EGL_NO_CONTEXT) { - ALOGE("eglCreateContext failed\n"); - return EGL_NO_CONTEXT; - } - - returnValue = eglMakeCurrent(display, surface, surface, context); - checkEglError("eglMakeCurrent", returnValue); - if (returnValue != EGL_TRUE) { - ALOGE("eglMakeCurrent failed\n"); - return EGL_NO_CONTEXT; - } - - return context; -} - void GLUtils::deleteTexture(GLuint* texture) { glDeleteTextures(1, texture); diff --git a/Source/WebCore/platform/graphics/android/rendering/GLUtils.h b/Source/WebCore/platform/graphics/android/rendering/GLUtils.h index d4a2e84..a235458 100644 --- a/Source/WebCore/platform/graphics/android/rendering/GLUtils.h +++ b/Source/WebCore/platform/graphics/android/rendering/GLUtils.h @@ -67,7 +67,6 @@ public: static bool isEGLFenceSyncSupported(); // Texture utilities - static EGLContext createBackgroundContext(EGLContext sharedContext); static void deleteTexture(GLuint* texture); static GLuint createSampleColorTexture(int r, int g, int b); static GLuint createSampleTexture(); diff --git a/Source/WebCore/platform/graphics/android/rendering/ShaderProgram.h b/Source/WebCore/platform/graphics/android/rendering/ShaderProgram.h index cfdccda..d93fd89 100644 --- a/Source/WebCore/platform/graphics/android/rendering/ShaderProgram.h +++ b/Source/WebCore/platform/graphics/android/rendering/ShaderProgram.h @@ -153,7 +153,7 @@ public: m_contrast = contrast; } void setGLDrawInfo(const android::uirenderer::DrawGlInfo* info); - + void forceNeedsInit() { m_needsInit = true; } bool needsInit() { return m_needsInit; } bool usePointSampling(float tileScale, const TransformationMatrix* layerTransform); diff --git a/Source/WebCore/platform/graphics/android/rendering/TilesManager.cpp b/Source/WebCore/platform/graphics/android/rendering/TilesManager.cpp index f46562a..731db23 100644 --- a/Source/WebCore/platform/graphics/android/rendering/TilesManager.cpp +++ b/Source/WebCore/platform/graphics/android/rendering/TilesManager.cpp @@ -96,6 +96,7 @@ TilesManager::TilesManager() , m_drawGLCount(1) , m_lastTimeLayersUsed(0) , m_hasLayerTextures(false) + , m_eglContext(EGL_NO_CONTEXT) { ALOGV("TilesManager ctor"); m_textures.reserveCapacity(MAX_TEXTURE_ALLOCATION); @@ -163,6 +164,14 @@ void TilesManager::discardTextures(bool allTextures, bool glTextures) discardTexturesVector(sparedDrawCount, m_tilesTextures, glTextures); } +void TilesManager::markAllGLTexturesZero() +{ + for (unsigned int i = 0; i < m_textures.size(); i++) + m_textures[i]->m_ownTextureId = 0; + for (unsigned int i = 0; i < m_tilesTextures.size(); i++) + m_tilesTextures[i]->m_ownTextureId = 0; +} + void TilesManager::discardTexturesVector(unsigned long long sparedDrawCount, WTF::Vector& textures, bool deallocateGLTextures) @@ -417,6 +426,55 @@ TransferQueue* TilesManager::transferQueue() return m_queue; } +// When GL context changed or we get a low memory signal, we want to cleanup all +// the GPU memory webview is using. +// The recreation will be on the next incoming draw call at the drawGL of +// GLWebViewState or the VideoLayerAndroid +void TilesManager::cleanupGLResources() +{ + transferQueue()->cleanupGLResourcesAndQueue(); + shader()->cleanupGLResources(); + videoLayerManager()->cleanupGLResources(); + m_eglContext = EGL_NO_CONTEXT; + GLUtils::checkGlError("TilesManager::cleanupGLResources"); +} + +void TilesManager::updateTilesIfContextVerified() +{ + if (updateContextIfChanged()) { + // A change in EGL context is an unexpected error, but we don't want to + // crash or ANR. Therefore, abandon the Surface Texture and GL resources; + // they'll be recreated later in setupDrawing. (We can't delete them + // since the context is gone) + transferQueue()->resetQueue(); + shader()->forceNeedsInit(); + videoLayerManager()->forceNeedsInit(); + markAllGLTexturesZero(); + } else { + // Here before we draw, update the Tile which has updated content. + // Inside this function, just do GPU blits from the transfer queue into + // the Tiles' texture. + transferQueue()->updateDirtyTiles(); + // Clean up GL textures for video layer. + videoLayerManager()->deleteUnusedTextures(); + } +} + +// Return true if context has changed, which indicate an error we should look +// into. +bool TilesManager::updateContextIfChanged() +{ + bool changed = false; + EGLContext ctx = eglGetCurrentContext(); + GLUtils::checkEglError("contextChanged"); + if (ctx != m_eglContext && m_eglContext != EGL_NO_CONTEXT) { + ALOGE("Unexpected : EGLContext changed! current %x , expected %x", ctx, m_eglContext); + changed = true; + } + m_eglContext = ctx; + return changed; +} + float TilesManager::tileWidth() { return TILE_WIDTH; diff --git a/Source/WebCore/platform/graphics/android/rendering/TilesManager.h b/Source/WebCore/platform/graphics/android/rendering/TilesManager.h index 92c56d3..17d44b5 100644 --- a/Source/WebCore/platform/graphics/android/rendering/TilesManager.h +++ b/Source/WebCore/platform/graphics/android/rendering/TilesManager.h @@ -69,6 +69,9 @@ public: TransferQueue* transferQueue(); VideoLayerManager* videoLayerManager() { return &m_videoLayerManager; } + void updateTilesIfContextVerified(); + void cleanupGLResources(); + void gatherTextures(); bool layerTexturesRemain() { return m_layerTexturesRemain; } void gatherTexturesNumbers(int* nbTextures, int* nbAllocatedTextures, @@ -163,6 +166,8 @@ private: void discardTexturesVector(unsigned long long sparedDrawCount, WTF::Vector& textures, bool deallocateGLTextures); + void markAllGLTexturesZero(); + bool updateContextIfChanged(); WTF::Vector m_textures; WTF::Vector m_availableTextures; @@ -201,6 +206,8 @@ private: unsigned long long m_drawGLCount; double m_lastTimeLayersUsed; bool m_hasLayerTextures; + + EGLContext m_eglContext; }; } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/android/rendering/TransferQueue.cpp b/Source/WebCore/platform/graphics/android/rendering/TransferQueue.cpp index 5ede167..559bd4f 100644 --- a/Source/WebCore/platform/graphics/android/rendering/TransferQueue.cpp +++ b/Source/WebCore/platform/graphics/android/rendering/TransferQueue.cpp @@ -75,14 +75,19 @@ TransferQueue::~TransferQueue() delete[] m_transferQueue; } +// Set the queue to be totally empty, abandon the Surface Texture. This should +// be called only when we hit a wrong EGL Context in an error situation. +void TransferQueue::resetQueue() +{ + android::Mutex::Autolock lock(m_transferQueueItemLocks); + emptyAndAbandonQueue(); + m_sharedSurfaceTextureId = 0; +} + // This should be called within the m_transferQueueItemLocks. // Now only called by emptyQueue() and destructor. void TransferQueue::cleanupGLResources() { - if (m_sharedSurfaceTexture.get()) { - m_sharedSurfaceTexture->abandon(); - m_sharedSurfaceTexture.clear(); - } if (m_fboID) { glDeleteFramebuffers(1, &m_fboID); m_fboID = 0; @@ -290,17 +295,27 @@ void TransferQueue::setHasGLContext(bool hasContext) m_hasGLContext = hasContext; } -void TransferQueue::setPendingDiscardWithLock() +// Call within a m_transferQueueItemLocks, now called by resetQueue() and +// cleanupGLResoucesAndQueue() +void TransferQueue::emptyAndAbandonQueue() { - android::Mutex::Autolock lock(m_transferQueueItemLocks); - setPendingDiscard(); + for (int i = 0 ; i < m_transferQueueSize; i++) + m_transferQueue[i].status = emptyItem; + m_emptyItemCount = m_transferQueueSize; + m_pureColorTileQueue.clear(); + + if (m_sharedSurfaceTexture.get()) { + m_sharedSurfaceTexture->abandon(); + m_sharedSurfaceTexture.clear(); + } + // This can prevent the tex gen thread to produce, until next incoming draw. + setHasGLContext(false); } -void TransferQueue::emptyQueue() +void TransferQueue::cleanupGLResourcesAndQueue() { android::Mutex::Autolock lock(m_transferQueueItemLocks); - setPendingDiscard(); - cleanupPendingDiscard(); + emptyAndAbandonQueue(); cleanupGLResources(); } diff --git a/Source/WebCore/platform/graphics/android/rendering/TransferQueue.h b/Source/WebCore/platform/graphics/android/rendering/TransferQueue.h index d1024a4..fe58336 100644 --- a/Source/WebCore/platform/graphics/android/rendering/TransferQueue.h +++ b/Source/WebCore/platform/graphics/android/rendering/TransferQueue.h @@ -117,7 +117,6 @@ public: // This will be called by the browser through nativeSetProperty void setTextureUploadType(TextureUploadType type); - void cleanupGLResources(); void updateDirtyTiles(); void initGLResources(int width, int height); @@ -140,10 +139,10 @@ public: void addItemInPureColorQueue(const TileRenderInfo* renderInfo); - void setPendingDiscardWithLock(); - void emptyQueue(); + void cleanupGLResourcesAndQueue(); bool needsInit() { return !m_sharedSurfaceTextureId; } + void resetQueue(); // This queue can be accessed from UI and TexGen thread, therefore, we need // a lock to protect its access TileTransferData* m_transferQueue; @@ -159,6 +158,7 @@ private: const SkBitmap& bitmap); bool getHasGLContext(); void setHasGLContext(bool hasContext); + void emptyAndAbandonQueue(); int getNextTransferQueueIndex(); @@ -174,6 +174,7 @@ private: // Before each draw call and the blit operation, clean up all the // pendingDiscard items. void cleanupPendingDiscard(); + void cleanupGLResources(); void blitTileFromQueue(GLuint fboID, TileTexture* destTex, TileTexture* frontTex, -- cgit v1.1