From f32b2ce68c45663b1193cfe45a10d2889fd61c81 Mon Sep 17 00:00:00 2001 From: Derek Sollenberger Date: Wed, 23 Mar 2011 11:19:05 -0400 Subject: Fix GPU leak where plugin textures in the WebKit thread were not being deleted. bug: 4165067 Change-Id: I2f50f600133300cec712b0177ead1a7afbbee2c3 --- .../graphics/android/DoubleBufferedTexture.cpp | 12 ++++ .../graphics/android/DoubleBufferedTexture.h | 1 + WebCore/platform/graphics/android/MediaLayer.cpp | 18 ++++- WebCore/platform/graphics/android/MediaLayer.h | 1 + WebCore/platform/graphics/android/MediaTexture.cpp | 79 ++++++++++++++++++++++ WebCore/platform/graphics/android/MediaTexture.h | 18 ++++- .../platform/graphics/android/SharedTexture.cpp | 21 +++++- WebCore/platform/graphics/android/SharedTexture.h | 1 + 8 files changed, 143 insertions(+), 8 deletions(-) (limited to 'WebCore') diff --git a/WebCore/platform/graphics/android/DoubleBufferedTexture.cpp b/WebCore/platform/graphics/android/DoubleBufferedTexture.cpp index 7dcd1bc..cf640e8 100644 --- a/WebCore/platform/graphics/android/DoubleBufferedTexture.cpp +++ b/WebCore/platform/graphics/android/DoubleBufferedTexture.cpp @@ -102,6 +102,18 @@ EGLContext DoubleBufferedTexture::producerAcquireContext() return context; } +void DoubleBufferedTexture::producerDeleteTextures() +{ + m_textureA.lock(); + m_textureB.lock(); + LOGV("Deleting Producer Textures A/B (%d:%d)", m_textureA.getSourceTextureId(), + m_textureB.getSourceTextureId()); + m_textureA.deleteSourceTexture(); + m_textureB.deleteSourceTexture(); + m_textureA.unlock(); + m_textureB.unlock(); +} + TextureInfo* DoubleBufferedTexture::producerLock() { SharedTexture* sharedTex = getWriteableTexture(); diff --git a/WebCore/platform/graphics/android/DoubleBufferedTexture.h b/WebCore/platform/graphics/android/DoubleBufferedTexture.h index 5016332..8a9d81e 100644 --- a/WebCore/platform/graphics/android/DoubleBufferedTexture.h +++ b/WebCore/platform/graphics/android/DoubleBufferedTexture.h @@ -42,6 +42,7 @@ public: virtual void producerRelease(); virtual void producerReleaseAndSwap(); EGLContext producerAcquireContext(); + void producerDeleteTextures(); // consumer thread functions TextureInfo* consumerLock(); diff --git a/WebCore/platform/graphics/android/MediaLayer.cpp b/WebCore/platform/graphics/android/MediaLayer.cpp index 40a0f11..1ba6d46 100644 --- a/WebCore/platform/graphics/android/MediaLayer.cpp +++ b/WebCore/platform/graphics/android/MediaLayer.cpp @@ -43,33 +43,45 @@ namespace WebCore { MediaLayer::MediaLayer(jobject weakWebViewRef) : LayerAndroid((RenderLayer*) NULL) { m_bufferedTexture = new MediaTexture(EGL_NO_CONTEXT); - m_bufferedTexture->incStrong(this); + m_bufferedTexture->producerInc(); m_videoTexture = new VideoTexture(weakWebViewRef); m_videoTexture->incStrong(this); + m_isCopy = false; m_currentTextureInfo = 0; m_isContentInverted = false; m_outlineSize = 0; XLOG("Creating Media Layer %p", this); + XLOG("producer: %d consumer: %d", m_bufferedTexture->getProducerCount(), + m_bufferedTexture->getConsumerCount()); } MediaLayer::MediaLayer(const MediaLayer& layer) : LayerAndroid(layer) { m_bufferedTexture = layer.getTexture(); - m_bufferedTexture->incStrong(this); + m_bufferedTexture->consumerInc(); m_videoTexture = layer.m_videoTexture; m_videoTexture->incStrong(this); + m_isCopy = true; m_currentTextureInfo = 0; m_isContentInverted = layer.m_isContentInverted; m_outlineSize = layer.m_outlineSize; XLOG("Creating Media Layer Copy %p -> %p", &layer, this); + XLOG("producer: %d consumer: %d COPY", m_bufferedTexture->getProducerCount(), + m_bufferedTexture->getConsumerCount()); } MediaLayer::~MediaLayer() { XLOG("Deleting Media Layer"); - m_bufferedTexture->decStrong(this); + XLOG("producer: %d consumer: %d %s", m_bufferedTexture->getProducerCount(), + m_bufferedTexture->getConsumerCount(), (m_isCopy) ? "COPY" : ""); + + if (m_isCopy) + m_bufferedTexture->consumerDec(); + else + m_bufferedTexture->producerDec(); m_videoTexture->decStrong(this); } diff --git a/WebCore/platform/graphics/android/MediaLayer.h b/WebCore/platform/graphics/android/MediaLayer.h index 46789cb..8a07134 100644 --- a/WebCore/platform/graphics/android/MediaLayer.h +++ b/WebCore/platform/graphics/android/MediaLayer.h @@ -57,6 +57,7 @@ public: void releaseNativeWindowForVideo(ANativeWindow* window); private: + bool m_isCopy; // Primary GL texture variables MediaTexture* m_bufferedTexture; diff --git a/WebCore/platform/graphics/android/MediaTexture.cpp b/WebCore/platform/graphics/android/MediaTexture.cpp index 9836a01..9821065 100644 --- a/WebCore/platform/graphics/android/MediaTexture.cpp +++ b/WebCore/platform/graphics/android/MediaTexture.cpp @@ -48,6 +48,85 @@ namespace WebCore { +MediaTexture::MediaTexture(EGLContext sharedContext) : DoubleBufferedTexture(sharedContext) +{ + m_producerRefCount = 0; + m_consumerRefCount = 0; +} + +/* Increment the number of objects in the producer's thread that hold a reference + * to this object. In practice, there is often only one producer reference for + * the lifetime of the object. + */ +void MediaTexture::producerInc() +{ + android::Mutex::Autolock lock(m_mediaLock); + m_producerRefCount++; +} + +/* Decrement the number of objects in the producer's thread that are holding a + * reference to this object. When removing the last reference we must cleanup + * all GL objects that are associated with the producer's thread. There may not + * be a consumer reference as the object may not have synced to the UI thread, + * in which case the producer needs to handle the deletion of the object. + */ +void MediaTexture::producerDec() +{ + bool needsDeleted = false; + + m_mediaLock.lock(); + m_producerRefCount--; + if (m_producerRefCount == 0) { + producerDeleteTextures(); + if (m_consumerRefCount < 1) { + XLOG("INFO: This texture has not been synced to the UI thread"); + needsDeleted = true; + } + } + m_mediaLock.unlock(); + + if (needsDeleted) { + XLOG("Deleting MediaTexture Object"); + delete this; + } +} + +/* Increment the number of objects in the consumer's thread that hold a reference + * to this object. In practice, there can be multiple producer references as the + * consumer (i.e. UI) thread may have multiple copies of the layer tree. + */ +void MediaTexture::consumerInc() +{ + android::Mutex::Autolock lock(m_mediaLock); + m_consumerRefCount++; +} + +/* Decrement the number of objects in the consumer's thread that are holding a + * reference to this object. When removing the last reference we must delete + * this object and by extension cleanup all GL objects that are associated with + * the consumer's thread. At the time of deletion there should be no remaining + * producer references. + */ +void MediaTexture::consumerDec() +{ + bool needsDeleted = false; + + m_mediaLock.lock(); + m_consumerRefCount--; + if (m_consumerRefCount == 0) { + needsDeleted = true; + if (m_producerRefCount != 0) { + XLOG("ERROR: We should not delete the consumer before the producer is finished"); + } + } + m_mediaLock.unlock(); + + if (needsDeleted) { + XLOG("Deleting MediaTexture Object"); + delete this; + } +} + VideoTexture::VideoTexture(jobject weakWebViewRef) : android::LightRefBase() { m_weakWebViewRef = weakWebViewRef; diff --git a/WebCore/platform/graphics/android/MediaTexture.h b/WebCore/platform/graphics/android/MediaTexture.h index b0c04a6..722afd8 100644 --- a/WebCore/platform/graphics/android/MediaTexture.h +++ b/WebCore/platform/graphics/android/MediaTexture.h @@ -33,11 +33,23 @@ namespace WebCore { class VideoListener; -class MediaTexture : public DoubleBufferedTexture, - public android::LightRefBase { +class MediaTexture : public DoubleBufferedTexture { public: - MediaTexture(EGLContext sharedContext) : DoubleBufferedTexture(sharedContext) { }; + MediaTexture(EGLContext sharedContext); + + void producerInc(); + void producerDec(); + void consumerInc(); + void consumerDec(); + + int getProducerCount() { android::Mutex::Autolock lock(m_mediaLock); return m_producerRefCount; } + int getConsumerCount() { android::Mutex::Autolock lock(m_mediaLock); return m_consumerRefCount; } + +private: + android::Mutex m_mediaLock; + int m_producerRefCount; + int m_consumerRefCount; }; class VideoTexture : public android::LightRefBase { diff --git a/WebCore/platform/graphics/android/SharedTexture.cpp b/WebCore/platform/graphics/android/SharedTexture.cpp index f2da663..8c703d4 100644 --- a/WebCore/platform/graphics/android/SharedTexture.cpp +++ b/WebCore/platform/graphics/android/SharedTexture.cpp @@ -77,8 +77,7 @@ SharedTexture::SharedTexture() // called by the consumer when it no longer wants to consume and after it has // terminated all providers. If EGLImages are used, the deletion of the -// source texture and EGLImage is the responsibility of the caller. In the case -// of double buffered textures this is handled in the onDestroy(...) method. +// source texture and EGLImage is the responsibility of the caller. SharedTexture::~SharedTexture() { if (m_supportsEGLImage) @@ -103,6 +102,24 @@ void SharedTexture::initSourceTexture() glGenTextures(1, &m_sourceTexture.m_textureId); } + +void SharedTexture::deleteSourceTexture() +{ + // We need to delete the source texture and EGLImage in the thread in which + // it was created. In theory we should be able to delete the EGLImage + // from either thread, but it currently throws an error if not deleted + // in the same EGLContext from which it was created. + if (m_supportsEGLImage) { + GLUtils::deleteTexture(&m_sourceTexture.m_textureId); + if (m_eglImage != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(eglGetCurrentDisplay(), m_eglImage); + m_eglImage = EGL_NO_IMAGE_KHR; + m_isNewImage = true; + } + LOGI("Deleted Source Texture and EGLImage"); + } +} + TextureInfo* SharedTexture::lockSource() { m_lock.lock(); diff --git a/WebCore/platform/graphics/android/SharedTexture.h b/WebCore/platform/graphics/android/SharedTexture.h index e654c5c..731aa5c 100644 --- a/WebCore/platform/graphics/android/SharedTexture.h +++ b/WebCore/platform/graphics/android/SharedTexture.h @@ -80,6 +80,7 @@ public: void unlock() { m_lock.unlock(); } void initSourceTexture(); // producer thread only + void deleteSourceTexture(); // producer thread only GLuint getSourceTextureId() { return m_sourceTexture.m_textureId; } GLuint getTargetTextureId() { return m_targetTexture.m_textureId; } EGLImageKHR getEGLImage() { return m_eglImage; } -- cgit v1.1