Android: Fix texture leak in QAndroidTextureVideoOutput
Simplify the ownership of the OpenGL resources and make it simpler to release individual resources as needed. Previously we would reset the texture handle without releasing it back to the system, making us leak texture each time the video output was reset. Also, be consistent with the type used for the texture handle. Task-number: QTBUG-54340 Change-Id: Ic3b3c7322677a909e51aa5983fa99ccf8b290302 Reviewed-by: Yoann Lopes <yoann.lopes@qt.io>
This commit is contained in:
committed by
Yoann Lopes
parent
95e1012c9d
commit
124987e4bc
@@ -60,6 +60,22 @@ static const GLfloat g_texture_data[] = {
|
|||||||
0.f, 1.f
|
0.f, 1.f
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void OpenGLResourcesDeleter::deleteTextureHelper(quint32 id)
|
||||||
|
{
|
||||||
|
if (id != 0)
|
||||||
|
glDeleteTextures(1, &id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLResourcesDeleter::deleteFboHelper(void *fbo)
|
||||||
|
{
|
||||||
|
delete reinterpret_cast<QOpenGLFramebufferObject *>(fbo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLResourcesDeleter::deleteShaderProgramHelper(void *prog)
|
||||||
|
{
|
||||||
|
delete reinterpret_cast<QOpenGLShaderProgram *>(prog);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class AndroidTextureVideoBuffer : public QAbstractVideoBuffer
|
class AndroidTextureVideoBuffer : public QAbstractVideoBuffer
|
||||||
{
|
{
|
||||||
@@ -124,40 +140,6 @@ private:
|
|||||||
QImage m_image;
|
QImage m_image;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class OpenGLResourcesDeleter : public QObject
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
OpenGLResourcesDeleter()
|
|
||||||
: m_textureID(0)
|
|
||||||
, m_fbo(0)
|
|
||||||
, m_program(0)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
~OpenGLResourcesDeleter()
|
|
||||||
{
|
|
||||||
glDeleteTextures(1, &m_textureID);
|
|
||||||
delete m_fbo;
|
|
||||||
delete m_program;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setTexture(quint32 id) {
|
|
||||||
if (m_textureID)
|
|
||||||
glDeleteTextures(1, &m_textureID);
|
|
||||||
|
|
||||||
m_textureID = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setFbo(QOpenGLFramebufferObject *fbo) { m_fbo = fbo; }
|
|
||||||
void setShaderProgram(QOpenGLShaderProgram *prog) { m_program = prog; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
quint32 m_textureID;
|
|
||||||
QOpenGLFramebufferObject *m_fbo;
|
|
||||||
QOpenGLShaderProgram *m_program;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
QAndroidTextureVideoOutput::QAndroidTextureVideoOutput(QObject *parent)
|
QAndroidTextureVideoOutput::QAndroidTextureVideoOutput(QObject *parent)
|
||||||
: QAndroidVideoOutput(parent)
|
: QAndroidVideoOutput(parent)
|
||||||
, m_surface(0)
|
, m_surface(0)
|
||||||
@@ -165,7 +147,6 @@ QAndroidTextureVideoOutput::QAndroidTextureVideoOutput(QObject *parent)
|
|||||||
, m_externalTex(0)
|
, m_externalTex(0)
|
||||||
, m_fbo(0)
|
, m_fbo(0)
|
||||||
, m_program(0)
|
, m_program(0)
|
||||||
, m_glDeleter(0)
|
|
||||||
, m_surfaceTextureCanAttachToContext(QtAndroidPrivate::androidSdkVersion() >= 16)
|
, m_surfaceTextureCanAttachToContext(QtAndroidPrivate::androidSdkVersion() >= 16)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -175,8 +156,12 @@ QAndroidTextureVideoOutput::~QAndroidTextureVideoOutput()
|
|||||||
{
|
{
|
||||||
clearSurfaceTexture();
|
clearSurfaceTexture();
|
||||||
|
|
||||||
if (m_glDeleter)
|
if (!m_glDeleter.isNull()) { // Make sure all of these are deleted on the render thread.
|
||||||
|
m_glDeleter->deleteFbo(m_fbo);
|
||||||
|
m_glDeleter->deleteShaderProgram(m_program);
|
||||||
|
m_glDeleter->deleteTexture(m_externalTex);
|
||||||
m_glDeleter->deleteLater();
|
m_glDeleter->deleteLater();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QAbstractVideoSurface *QAndroidTextureVideoOutput::surface() const
|
QAbstractVideoSurface *QAndroidTextureVideoOutput::surface() const
|
||||||
@@ -223,8 +208,7 @@ bool QAndroidTextureVideoOutput::initSurfaceTexture()
|
|||||||
// for the GL render thread to call us back to do it.
|
// for the GL render thread to call us back to do it.
|
||||||
if (QOpenGLContext::currentContext()) {
|
if (QOpenGLContext::currentContext()) {
|
||||||
glGenTextures(1, &m_externalTex);
|
glGenTextures(1, &m_externalTex);
|
||||||
m_glDeleter = new OpenGLResourcesDeleter;
|
m_glDeleter.reset(new OpenGLResourcesDeleter);
|
||||||
m_glDeleter->setTexture(m_externalTex);
|
|
||||||
} else if (!m_externalTex) {
|
} else if (!m_externalTex) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -239,10 +223,9 @@ bool QAndroidTextureVideoOutput::initSurfaceTexture()
|
|||||||
} else {
|
} else {
|
||||||
delete m_surfaceTexture;
|
delete m_surfaceTexture;
|
||||||
m_surfaceTexture = 0;
|
m_surfaceTexture = 0;
|
||||||
if (m_glDeleter)
|
if (!m_glDeleter.isNull())
|
||||||
m_glDeleter->deleteLater();
|
m_glDeleter->deleteTexture(m_externalTex);
|
||||||
m_externalTex = 0;
|
m_externalTex = 0;
|
||||||
m_glDeleter = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_surfaceTexture != 0;
|
return m_surfaceTexture != 0;
|
||||||
@@ -257,8 +240,14 @@ void QAndroidTextureVideoOutput::clearSurfaceTexture()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Also reset the attached OpenGL texture
|
// Also reset the attached OpenGL texture
|
||||||
if (m_surfaceTextureCanAttachToContext)
|
// Note: The Android SurfaceTexture class does not release the texture on deletion,
|
||||||
|
// only if detachFromGLContext() called (API level >= 16), so we'll do it manually,
|
||||||
|
// on the render thread.
|
||||||
|
if (m_surfaceTextureCanAttachToContext) {
|
||||||
|
if (!m_glDeleter.isNull())
|
||||||
|
m_glDeleter->deleteTexture(m_externalTex);
|
||||||
m_externalTex = 0;
|
m_externalTex = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AndroidSurfaceTexture *QAndroidTextureVideoOutput::surfaceTexture()
|
AndroidSurfaceTexture *QAndroidTextureVideoOutput::surfaceTexture()
|
||||||
@@ -388,19 +377,17 @@ void QAndroidTextureVideoOutput::createGLResources()
|
|||||||
Q_ASSERT(QOpenGLContext::currentContext() != NULL);
|
Q_ASSERT(QOpenGLContext::currentContext() != NULL);
|
||||||
|
|
||||||
if (!m_glDeleter)
|
if (!m_glDeleter)
|
||||||
m_glDeleter = new OpenGLResourcesDeleter;
|
m_glDeleter.reset(new OpenGLResourcesDeleter);
|
||||||
|
|
||||||
if (m_surfaceTextureCanAttachToContext && !m_externalTex) {
|
if (m_surfaceTextureCanAttachToContext && !m_externalTex) {
|
||||||
m_surfaceTexture->detachFromGLContext();
|
m_surfaceTexture->detachFromGLContext();
|
||||||
glGenTextures(1, &m_externalTex);
|
glGenTextures(1, &m_externalTex);
|
||||||
m_surfaceTexture->attachToGLContext(m_externalTex);
|
m_surfaceTexture->attachToGLContext(m_externalTex);
|
||||||
m_glDeleter->setTexture(m_externalTex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_fbo || m_fbo->size() != m_nativeSize) {
|
if (!m_fbo || m_fbo->size() != m_nativeSize) {
|
||||||
delete m_fbo;
|
delete m_fbo;
|
||||||
m_fbo = new QOpenGLFramebufferObject(m_nativeSize);
|
m_fbo = new QOpenGLFramebufferObject(m_nativeSize);
|
||||||
m_glDeleter->setFbo(m_fbo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_program) {
|
if (!m_program) {
|
||||||
@@ -431,8 +418,6 @@ void QAndroidTextureVideoOutput::createGLResources()
|
|||||||
m_program->bindAttributeLocation("vertexCoordsArray", 0);
|
m_program->bindAttributeLocation("vertexCoordsArray", 0);
|
||||||
m_program->bindAttributeLocation("textureCoordArray", 1);
|
m_program->bindAttributeLocation("textureCoordArray", 1);
|
||||||
m_program->link();
|
m_program->link();
|
||||||
|
|
||||||
m_glDeleter->setShaderProgram(m_program);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -442,8 +427,7 @@ void QAndroidTextureVideoOutput::customEvent(QEvent *e)
|
|||||||
// This is running in the render thread (OpenGL enabled)
|
// This is running in the render thread (OpenGL enabled)
|
||||||
if (!m_surfaceTextureCanAttachToContext && !m_externalTex) {
|
if (!m_surfaceTextureCanAttachToContext && !m_externalTex) {
|
||||||
glGenTextures(1, &m_externalTex);
|
glGenTextures(1, &m_externalTex);
|
||||||
m_glDeleter = new OpenGLResourcesDeleter; // will cleanup GL resources in the correct thread
|
m_glDeleter.reset(new OpenGLResourcesDeleter); // We'll use this to cleanup GL resources in the correct thread
|
||||||
m_glDeleter->setTexture(m_externalTex);
|
|
||||||
emit readyChanged(true);
|
emit readyChanged(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ class AndroidSurfaceTexture;
|
|||||||
class AndroidSurfaceHolder;
|
class AndroidSurfaceHolder;
|
||||||
class QOpenGLFramebufferObject;
|
class QOpenGLFramebufferObject;
|
||||||
class QOpenGLShaderProgram;
|
class QOpenGLShaderProgram;
|
||||||
class OpenGLResourcesDeleter;
|
|
||||||
class QAbstractVideoSurface;
|
class QAbstractVideoSurface;
|
||||||
|
|
||||||
class QAndroidVideoOutput : public QObject
|
class QAndroidVideoOutput : public QObject
|
||||||
@@ -69,6 +68,19 @@ protected:
|
|||||||
QAndroidVideoOutput(QObject *parent) : QObject(parent) { }
|
QAndroidVideoOutput(QObject *parent) : QObject(parent) { }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class OpenGLResourcesDeleter : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
void deleteTexture(quint32 id) { QMetaObject::invokeMethod(this, "deleteTextureHelper", Qt::AutoConnection, Q_ARG(quint32, id)); }
|
||||||
|
void deleteFbo(QOpenGLFramebufferObject *fbo) { QMetaObject::invokeMethod(this, "deleteFboHelper", Qt::AutoConnection, Q_ARG(void *, fbo)); }
|
||||||
|
void deleteShaderProgram(QOpenGLShaderProgram *prog) { QMetaObject::invokeMethod(this, "deleteShaderProgramHelper", Qt::AutoConnection, Q_ARG(void *, prog)); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Q_INVOKABLE void deleteTextureHelper(quint32 id);
|
||||||
|
Q_INVOKABLE void deleteFboHelper(void *fbo);
|
||||||
|
Q_INVOKABLE void deleteShaderProgramHelper(void *prog);
|
||||||
|
};
|
||||||
|
|
||||||
class QAndroidTextureVideoOutput : public QAndroidVideoOutput
|
class QAndroidTextureVideoOutput : public QAndroidVideoOutput
|
||||||
{
|
{
|
||||||
@@ -108,7 +120,7 @@ private:
|
|||||||
quint32 m_externalTex;
|
quint32 m_externalTex;
|
||||||
QOpenGLFramebufferObject *m_fbo;
|
QOpenGLFramebufferObject *m_fbo;
|
||||||
QOpenGLShaderProgram *m_program;
|
QOpenGLShaderProgram *m_program;
|
||||||
OpenGLResourcesDeleter *m_glDeleter;
|
QScopedPointer<OpenGLResourcesDeleter, QScopedPointerDeleteLater> m_glDeleter;
|
||||||
|
|
||||||
bool m_surfaceTextureCanAttachToContext;
|
bool m_surfaceTextureCanAttachToContext;
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ static void notifyFrameAvailable(JNIEnv* , jobject, jlong id)
|
|||||||
Q_EMIT obj->frameAvailable();
|
Q_EMIT obj->frameAvailable();
|
||||||
}
|
}
|
||||||
|
|
||||||
AndroidSurfaceTexture::AndroidSurfaceTexture(unsigned int texName)
|
AndroidSurfaceTexture::AndroidSurfaceTexture(quint32 texName)
|
||||||
: QObject()
|
: QObject()
|
||||||
{
|
{
|
||||||
Q_STATIC_ASSERT(sizeof (jlong) >= sizeof (void *));
|
Q_STATIC_ASSERT(sizeof (jlong) >= sizeof (void *));
|
||||||
@@ -157,7 +157,7 @@ jobject AndroidSurfaceTexture::surfaceHolder()
|
|||||||
return m_surfaceHolder.object();
|
return m_surfaceHolder.object();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidSurfaceTexture::attachToGLContext(int texName)
|
void AndroidSurfaceTexture::attachToGLContext(quint32 texName)
|
||||||
{
|
{
|
||||||
if (QtAndroidPrivate::androidSdkVersion() < 16 || !m_surfaceTexture.isValid())
|
if (QtAndroidPrivate::androidSdkVersion() < 16 || !m_surfaceTexture.isValid())
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class AndroidSurfaceTexture : public QObject
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit AndroidSurfaceTexture(unsigned int texName);
|
explicit AndroidSurfaceTexture(quint32 texName);
|
||||||
~AndroidSurfaceTexture();
|
~AndroidSurfaceTexture();
|
||||||
|
|
||||||
jobject surfaceTexture();
|
jobject surfaceTexture();
|
||||||
@@ -57,7 +57,7 @@ public:
|
|||||||
void release(); // API level 14
|
void release(); // API level 14
|
||||||
void updateTexImage();
|
void updateTexImage();
|
||||||
|
|
||||||
void attachToGLContext(int texName); // API level 16
|
void attachToGLContext(quint32 texName); // API level 16
|
||||||
void detachFromGLContext(); // API level 16
|
void detachFromGLContext(); // API level 16
|
||||||
|
|
||||||
static bool initJNI(JNIEnv *env);
|
static bool initJNI(JNIEnv *env);
|
||||||
|
|||||||
Reference in New Issue
Block a user