diff options
author | Sebastian Dröge <sebastian@centricular.com> | 2020-11-03 15:58:30 +0200 |
---|---|---|
committer | Tim-Philipp Müller <tim@centricular.com> | 2021-06-11 10:46:13 +0100 |
commit | 20cb300706d16d570bd93322fd3b7ad6664daa34 (patch) | |
tree | 066da44a78bca722e41649d54051fd1bcacc1e37 | |
parent | beba0254d8aa60f5f03eedacaec2955a6c896c31 (diff) | |
download | gstreamer-plugins-good-20cb300706d16d570bd93322fd3b7ad6664daa34.tar.gz |
qmlglsink: Keep old buffers around a bit longer if they were bound by QML
We don't know exactly when QML will stop using them but it should be
safe to unref them after at least 2 more buffers were bound.
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/merge_requests/1009>
-rw-r--r-- | ext/qt/gstqsgtexture.cc | 19 | ||||
-rw-r--r-- | ext/qt/gstqsgtexture.h | 2 | ||||
-rw-r--r-- | ext/qt/qtitem.cc | 54 |
3 files changed, 75 insertions, 0 deletions
diff --git a/ext/qt/gstqsgtexture.cc b/ext/qt/gstqsgtexture.cc index bfa79cda0..00e2ddad0 100644 --- a/ext/qt/gstqsgtexture.cc +++ b/ext/qt/gstqsgtexture.cc @@ -47,6 +47,7 @@ GstQSGTexture::GstQSGTexture () gst_video_info_init (&this->v_info); this->buffer_ = NULL; + this->buffer_was_bound = FALSE; this->qt_context_ = NULL; this->sync_buffer_ = gst_buffer_new (); this->dummy_tex_id_ = 0; @@ -56,6 +57,7 @@ GstQSGTexture::~GstQSGTexture () { gst_buffer_replace (&this->buffer_, NULL); gst_buffer_replace (&this->sync_buffer_, NULL); + this->buffer_was_bound = FALSE; if (this->dummy_tex_id_ && QOpenGLContext::currentContext ()) { QOpenGLContext::currentContext ()->functions ()->glDeleteTextures (1, &this->dummy_tex_id_); @@ -80,11 +82,26 @@ GstQSGTexture::setBuffer (GstBuffer * buffer) if (!gst_buffer_replace (&this->buffer_, buffer)) return FALSE; + this->buffer_was_bound = FALSE; this->qt_context_ = gst_gl_context_get_current (); return TRUE; } +/* only called from the streaming thread with scene graph thread blocked */ +GstBuffer * +GstQSGTexture::getBuffer (gboolean * was_bound) +{ + GstBuffer *buffer = NULL; + + if (this->buffer_) + buffer = gst_buffer_ref (this->buffer_); + if (was_bound) + *was_bound = this->buffer_was_bound; + + return buffer; +} + /* only called from qt's scene graph render thread */ void GstQSGTexture::bind () @@ -142,6 +159,8 @@ GstQSGTexture::bind () * to use the dummy texture */ use_dummy_tex = FALSE; + this->buffer_was_bound = TRUE; + out: if (G_UNLIKELY (use_dummy_tex)) { QOpenGLContext *qglcontext = QOpenGLContext::currentContext (); diff --git a/ext/qt/gstqsgtexture.h b/ext/qt/gstqsgtexture.h index fdabe93a9..ec4a16f57 100644 --- a/ext/qt/gstqsgtexture.h +++ b/ext/qt/gstqsgtexture.h @@ -38,6 +38,7 @@ public: void setCaps (GstCaps * caps); gboolean setBuffer (GstBuffer * buffer); + GstBuffer * getBuffer (gboolean * was_bound); /* QSGTexture */ void bind (); @@ -48,6 +49,7 @@ public: private: GstBuffer * buffer_; + gboolean buffer_was_bound; GstBuffer * sync_buffer_; GstGLContext * qt_context_; GstMemory * mem_; diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc index 8384a629a..dad549e76 100644 --- a/ext/qt/qtitem.cc +++ b/ext/qt/qtitem.cc @@ -79,6 +79,14 @@ struct _QtGLVideoItemPrivate QOpenGLContext *qt_context; GstGLContext *other_context; GstGLContext *context; + + /* buffers with textures that were bound by QML */ + GQueue bound_buffers; + /* buffers that were previously bound but in the meantime a new one was + * bound so this one is most likely not used anymore + * FIXME: Ideally we would use fences for this but there seems to be no + * way to reliably "try wait" on a fence */ + GQueue potentially_unbound_buffers; }; class InitializeSceneGraph : public QRunnable @@ -205,6 +213,9 @@ QSGNode * QtGLVideoItem::updatePaintNode(QSGNode * oldNode, UpdatePaintNodeData * updatePaintNodeData) { + GstBuffer *old_buffer; + gboolean was_bound = FALSE; + if (!m_openGlContextInitialized) { return oldNode; } @@ -232,6 +243,38 @@ QtGLVideoItem::updatePaintNode(QSGNode * oldNode, } tex = static_cast<GstQSGTexture *> (texNode->texture()); + + if ((old_buffer = tex->getBuffer(&was_bound))) { + if (old_buffer == this->priv->buffer) { + /* same buffer */ + gst_buffer_unref (old_buffer); + } else if (!was_bound) { + GST_TRACE ("old buffer %p was not bound yet, unreffing", old_buffer); + gst_buffer_unref (old_buffer); + } else { + GstBuffer *tmp_buffer; + + GST_TRACE ("old buffer %p was bound, queueing up for later", old_buffer); + /* Unref all buffers that were previously not bound anymore. At least + * one more buffer was bound in the meantime so this one is most likely + * not in use anymore. */ + while ((tmp_buffer = (GstBuffer*) g_queue_pop_head (&this->priv->potentially_unbound_buffers))) { + GST_TRACE ("old buffer %p should be unbound now, unreffing", tmp_buffer); + gst_buffer_unref (tmp_buffer); + } + + /* Move previous bound buffers to the next queue. We now know that + * another buffer was bound in the meantime and will free them on + * the next iteration above. */ + while ((tmp_buffer = (GstBuffer*) g_queue_pop_head (&this->priv->bound_buffers))) { + GST_TRACE ("old buffer %p is potentially unbound now", tmp_buffer); + g_queue_push_tail (&this->priv->potentially_unbound_buffers, tmp_buffer); + } + g_queue_push_tail (&this->priv->bound_buffers, old_buffer); + } + old_buffer = NULL; + } + tex->setCaps (this->priv->caps); tex->setBuffer (this->priv->buffer); texNode->markDirty(QSGNode::DirtyMaterial); @@ -263,12 +306,23 @@ QtGLVideoItem::updatePaintNode(QSGNode * oldNode, static void _reset (QtGLVideoItem * qt_item) { + GstBuffer *tmp_buffer; + gst_buffer_replace (&qt_item->priv->buffer, NULL); gst_caps_replace (&qt_item->priv->caps, NULL); qt_item->priv->negotiated = FALSE; qt_item->priv->initted = FALSE; + + while ((tmp_buffer = (GstBuffer*) g_queue_pop_head (&qt_item->priv->potentially_unbound_buffers))) { + GST_TRACE ("old buffer %p should be unbound now, unreffing", tmp_buffer); + gst_buffer_unref (tmp_buffer); + } + while ((tmp_buffer = (GstBuffer*) g_queue_pop_head (&qt_item->priv->bound_buffers))) { + GST_TRACE ("old buffer %p should be unbound now, unreffing", tmp_buffer); + gst_buffer_unref (tmp_buffer); + } } void |