diff options
author | Dominik Holland <dominik.holland@pelagicore.com> | 2016-03-02 11:27:46 +0100 |
---|---|---|
committer | Dominik Holland <dominik.holland@pelagicore.com> | 2016-05-18 15:08:53 +0000 |
commit | c7832b0a1d4e5e095e56315c43145bb8964f7030 (patch) | |
tree | 30e63aa55221255e9d759be7cdaae924e6e0798e | |
parent | 959d9d604da402e768ce329d43510d2b03756d89 (diff) | |
download | qtwayland-c7832b0a1d4e5e095e56315c43145bb8964f7030.tar.gz |
Fixed eglStream compositing
When creating the eglStream from the fd we also need to create
the texture and call stream_consumer_gltexture. Otherwise
the wayland client can't create the wayland client surface.
Fixed the qwindow-compositor example to use the texture associated
to the QWaylandBufferRef when compositing a EXTERNAL_OES target.
QML compositing only works when QSG_RENDER_LOOP is set to basic
as we need to generate a texture from the gui thread.
As the texture is created by the bufferintegration it might end up
in a different gl context than the quick item using it. The texture
is also deleted together with the buffer, which prevents the use
of the texture afterwards. Both problems need to be fixed in follow
up commits.
Task-number: QTBUG-50850
Change-Id: Ifec67bbe9e4b2a680c871dc4aced37b71b7b6f80
Reviewed-by: Louai Al-Khanji <louai.al-khanji@qt.io>
Reviewed-by: Giulio Camuffo <giulio.camuffo@kdab.com>
12 files changed, 165 insertions, 25 deletions
diff --git a/examples/wayland/qwindow-compositor/compositorwindow.cpp b/examples/wayland/qwindow-compositor/compositorwindow.cpp index 7e10a173..537a3d08 100644 --- a/examples/wayland/qwindow-compositor/compositorwindow.cpp +++ b/examples/wayland/qwindow-compositor/compositorwindow.cpp @@ -116,10 +116,18 @@ void CompositorWindow::paintGL() functions->glEnable(GL_BLEND); functions->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + GLenum currentTarget = GL_TEXTURE_2D; Q_FOREACH (WindowCompositorView *view, m_compositor->views()) { if (view->isCursor()) continue; - GLuint textureId = view->getTexture(); + GLenum target; + GLuint textureId = view->getTexture(&target); + if (!textureId || !target) + continue; + if (target != currentTarget) { + currentTarget = target; + m_textureBlitter.bind(currentTarget); + } QWaylandSurface *surface = view->surface(); if (surface && surface->isMapped()) { QSize s = surface->size(); diff --git a/examples/wayland/qwindow-compositor/windowcompositor.cpp b/examples/wayland/qwindow-compositor/windowcompositor.cpp index bd39908e..ecbbc504 100644 --- a/examples/wayland/qwindow-compositor/windowcompositor.cpp +++ b/examples/wayland/qwindow-compositor/windowcompositor.cpp @@ -50,25 +50,42 @@ #include <QtWaylandCompositor/qwaylanddrag.h> #include <QDebug> +#include <QOpenGLContext> + +#ifndef GL_TEXTURE_EXTERNAL_OES +#define GL_TEXTURE_EXTERNAL_OES 0x8D65 +#endif WindowCompositorView::WindowCompositorView() - : m_texture(0) + : m_textureTarget(GL_TEXTURE_2D) + , m_texture(0) , m_wlShellSurface(nullptr) , m_xdgSurface(nullptr) , m_xdgPopup(nullptr) , m_parentView(nullptr) {} -GLuint WindowCompositorView::getTexture() { +GLuint WindowCompositorView::getTexture(GLenum *target) +{ + QWaylandBufferRef buf = currentBuffer(); + m_texture = buf.textureForPlane(0); + + if (buf.bufferFormatEgl() == QWaylandBufferRef::BufferFormatEgl_EXTERNAL_OES) + m_textureTarget = GL_TEXTURE_EXTERNAL_OES; + if (advance()) { - if (m_texture) - glDeleteTextures(1, &m_texture); + if (!m_texture) + glGenTextures(1, &m_texture); - glGenTextures(1, &m_texture); - glBindTexture(GL_TEXTURE_2D, m_texture); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - currentBuffer().bindToTexture(); + glBindTexture(m_textureTarget, m_texture); + buf.bindToTexture(); } + + buf.updateTexture(); + + if (target) + *target = m_textureTarget; + return m_texture; } diff --git a/examples/wayland/qwindow-compositor/windowcompositor.h b/examples/wayland/qwindow-compositor/windowcompositor.h index 5290d7c8..80a7bebe 100644 --- a/examples/wayland/qwindow-compositor/windowcompositor.h +++ b/examples/wayland/qwindow-compositor/windowcompositor.h @@ -59,7 +59,7 @@ class WindowCompositorView : public QWaylandView Q_OBJECT public: WindowCompositorView(); - GLuint getTexture(); + GLuint getTexture(GLenum *target = 0); QPointF position() const { return m_position; } void setPosition(const QPointF &pos) { m_position = pos; } bool isCursor() const; @@ -72,6 +72,7 @@ public: private: friend class WindowCompositor; + GLenum m_textureTarget; GLuint m_texture; QPointF m_position; QWaylandWlShellSurface *m_wlShellSurface; diff --git a/src/compositor/compositor_api/qwaylandbufferref.cpp b/src/compositor/compositor_api/qwaylandbufferref.cpp index 929503b4..e9fbcfbd 100644 --- a/src/compositor/compositor_api/qwaylandbufferref.cpp +++ b/src/compositor/compositor_api/qwaylandbufferref.cpp @@ -241,6 +241,16 @@ QImage QWaylandBufferRef::image() const return d->buffer->image(); } +#ifdef QT_COMPOSITOR_WAYLAND_GL +GLuint QWaylandBufferRef::textureForPlane(int plane) const +{ + if (d->nullOrDestroyed()) + return 0; + + return d->buffer->textureForPlane(plane); +} +#endif + /*! * Binds the buffer to the current OpenGL texture. This may * perform a copy of the buffer data, depending on the platform diff --git a/src/compositor/compositor_api/qwaylandbufferref.h b/src/compositor/compositor_api/qwaylandbufferref.h index 4ded8f17..50c85b96 100644 --- a/src/compositor/compositor_api/qwaylandbufferref.h +++ b/src/compositor/compositor_api/qwaylandbufferref.h @@ -96,6 +96,9 @@ public: bool isShm() const; QImage image() const; +#ifdef QT_COMPOSITOR_WAYLAND_GL + GLuint textureForPlane(int plane) const; +#endif void bindToTexture() const; void updateTexture() const; diff --git a/src/compositor/compositor_api/qwaylandquickitem.cpp b/src/compositor/compositor_api/qwaylandquickitem.cpp index 9fa81cfb..ab11ff4f 100644 --- a/src/compositor/compositor_api/qwaylandquickitem.cpp +++ b/src/compositor/compositor_api/qwaylandquickitem.cpp @@ -179,17 +179,6 @@ QWaylandBufferMaterial::QWaylandBufferMaterial(QWaylandBufferRef::BufferFormatEg { QOpenGLFunctions *gl = QOpenGLContext::currentContext()->functions(); - for (int i = 0; i < bufferTypes[m_format].planeCount; i++) { - GLuint texture; - gl->glGenTextures(1, &texture); - gl->glBindTexture(bufferTypes[m_format].textureTarget, texture); - gl->glTexParameteri(bufferTypes[m_format].textureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - gl->glTexParameteri(bufferTypes[m_format].textureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - gl->glTexParameteri(bufferTypes[m_format].textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - gl->glTexParameteri(bufferTypes[m_format].textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - m_textures << texture; - } - gl->glBindTexture(bufferTypes[m_format].textureTarget, 0); setFlag(bufferTypes[m_format].materialFlags); } @@ -202,11 +191,36 @@ QWaylandBufferMaterial::~QWaylandBufferMaterial() gl->glDeleteTextures(1, &texture); } +void QWaylandBufferMaterial::setTextureForPlane(int plane, uint texture) +{ + if (plane < 0 || plane >= bufferTypes[m_format].planeCount) { + qWarning("plane index is out of range"); + return; + } + + QOpenGLFunctions *gl = QOpenGLContext::currentContext()->functions(); + const GLenum target = bufferTypes[m_format].textureTarget; + + gl->glBindTexture(target, texture); + setTextureParameters(target); + + ensureTextures(plane - 1); + + if (m_textures.size() <= plane) { + m_textures << texture; + } else { + std::swap(m_textures[plane], texture); + gl->glDeleteTextures(1, &texture); + } +} + void QWaylandBufferMaterial::bind() { QOpenGLFunctions *gl = QOpenGLContext::currentContext()->functions(); const GLenum target = bufferTypes[m_format].textureTarget; + ensureTextures(bufferTypes[m_format].planeCount); + switch (m_textures.size()) { case 3: gl->glActiveTexture(GL_TEXTURE2); @@ -230,6 +244,31 @@ QSGMaterialShader *QWaylandBufferMaterial::createShader() const return new QWaylandBufferMaterialShader(m_format); } + +void QWaylandBufferMaterial::setTextureParameters(GLenum target) +{ + QOpenGLFunctions *gl = QOpenGLContext::currentContext()->functions(); + gl->glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl->glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl->glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl->glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +} + +//TODO move this into a separate centralized texture management class +void QWaylandBufferMaterial::ensureTextures(int count) +{ + QOpenGLFunctions *gl = QOpenGLContext::currentContext()->functions(); + const GLenum target = bufferTypes[m_format].textureTarget; + GLuint texture; + + for (int plane = m_textures.size(); plane < count; plane++) { + gl->glGenTextures(1, &texture); + gl->glBindTexture(target, texture); + setTextureParameters(target); + m_textures << texture; + } +} + QMutex *QWaylandQuickItemPrivate::mutex = 0; class QWaylandSurfaceTextureProvider : public QSGTextureProvider @@ -1047,6 +1086,9 @@ QSGNode *QWaylandQuickItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDat if (d->newTexture) { d->newTexture = false; + for (int plane = 0; plane < bufferTypes[ref.bufferFormatEgl()].planeCount; plane++) + if (uint texture = ref.textureForPlane(plane)) + material->setTextureForPlane(plane, texture); material->bind(); ref.bindToTexture(); } diff --git a/src/compositor/compositor_api/qwaylandquickitem_p.h b/src/compositor/compositor_api/qwaylandquickitem_p.h index 679d218e..b529ba95 100644 --- a/src/compositor/compositor_api/qwaylandquickitem_p.h +++ b/src/compositor/compositor_api/qwaylandquickitem_p.h @@ -85,12 +85,17 @@ public: QWaylandBufferMaterial(QWaylandBufferRef::BufferFormatEgl format); ~QWaylandBufferMaterial(); + void setTextureForPlane(int plane, uint texture); + void bind(); QSGMaterialType *type() const Q_DECL_OVERRIDE; QSGMaterialShader *createShader() const Q_DECL_OVERRIDE; private: + void setTextureParameters(GLenum target); + void ensureTextures(int count); + const QWaylandBufferRef::BufferFormatEgl m_format; QVarLengthArray<GLuint, 3> m_textures; }; diff --git a/src/compositor/hardware_integration/qwlclientbufferintegration_p.h b/src/compositor/hardware_integration/qwlclientbufferintegration_p.h index 1fbcb799..90762437 100644 --- a/src/compositor/hardware_integration/qwlclientbufferintegration_p.h +++ b/src/compositor/hardware_integration/qwlclientbufferintegration_p.h @@ -73,6 +73,7 @@ public: virtual void initializeBuffer(struct ::wl_resource *buffer) { Q_UNUSED(buffer); } virtual QWaylandBufferRef::BufferFormatEgl bufferFormat(struct ::wl_resource *buffer) { Q_UNUSED(buffer); return QWaylandBufferRef::BufferFormatEgl_RGBA; } + virtual uint textureForBuffer(struct ::wl_resource *buffer, int plane) { Q_UNUSED(buffer); Q_UNUSED(plane); return 0; } virtual void bindTextureToBuffer(struct ::wl_resource *buffer) = 0; virtual void updateTextureForBuffer(struct ::wl_resource *buffer) { Q_UNUSED(buffer); } diff --git a/src/compositor/wayland_wrapper/qwlsurfacebuffer.cpp b/src/compositor/wayland_wrapper/qwlsurfacebuffer.cpp index c7bdbcee..14ccde13 100644 --- a/src/compositor/wayland_wrapper/qwlsurfacebuffer.cpp +++ b/src/compositor/wayland_wrapper/qwlsurfacebuffer.cpp @@ -232,6 +232,14 @@ void SurfaceBuffer::bindToTexture() const } } +uint SurfaceBuffer::textureForPlane(int plane) const +{ + if (QtWayland::ClientBufferIntegration *clientInt = QWaylandCompositorPrivate::get(m_compositor)->clientBufferIntegration()) + return clientInt->textureForBuffer(m_buffer, plane); + + return 0; +} + void SurfaceBuffer::updateTexture() const { if (QtWayland::ClientBufferIntegration *clientInt = QWaylandCompositorPrivate::get(m_compositor)->clientBufferIntegration()) diff --git a/src/compositor/wayland_wrapper/qwlsurfacebuffer_p.h b/src/compositor/wayland_wrapper/qwlsurfacebuffer_p.h index 85f78d41..95e7e815 100644 --- a/src/compositor/wayland_wrapper/qwlsurfacebuffer_p.h +++ b/src/compositor/wayland_wrapper/qwlsurfacebuffer_p.h @@ -106,6 +106,7 @@ public: QImage image() const; QWaylandBufferRef::BufferFormatEgl bufferFormatEgl() const; void bindToTexture() const; + uint textureForPlane(int plane) const; void updateTexture() const; static bool hasContent(SurfaceBuffer *buffer) { return buffer && buffer->waylandBufferHandle(); } diff --git a/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.cpp b/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.cpp index e6b68225..1c009d34 100644 --- a/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.cpp +++ b/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.cpp @@ -117,6 +117,7 @@ struct BufferState EGLint egl_format; QVarLengthArray<EGLImageKHR, 3> egl_images; EGLStreamKHR egl_stream; + GLuint eglstream_texture; bool isYInverted; QSize size; @@ -167,6 +168,7 @@ struct buffer_destroy_listener : wl_listener BufferState::BufferState() : egl_format(EGL_TEXTURE_RGBA) , egl_stream(EGL_NO_STREAM_KHR) + , eglstream_texture(0) , isYInverted(true) {} @@ -269,7 +271,21 @@ void WaylandEglClientBufferIntegrationPrivate::attach_egl_fd_texture(struct ::wl return; } + if (!QOpenGLContext::currentContext()) + qWarning("EglClientBufferIntegration: creating texture with no current context"); + + //TODO This texture might end up in a different context than the quick item which wants to use it, this needs to be fixed somehow. + glGenTextures(1, &state.eglstream_texture); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_EXTERNAL_OES, state.eglstream_texture); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + register_buffer(buffer, state); + + bindBuffer(buffer); } void WaylandEglClientBufferIntegrationPrivate::register_buffer(struct ::wl_resource *buffer, BufferState state) @@ -301,8 +317,12 @@ void WaylandEglClientBufferIntegrationPrivate::bindBuffer(struct ::wl_resource * const BufferState state = buffers.value(buffer); if (state.egl_stream != EGL_NO_STREAM_KHR) { - if (funcs->stream_consumer_gltexture(egl_display, state.egl_stream) != EGL_TRUE) - qWarning("%s:%d: eglStreamConsumerGLTextureExternalKHR failed: 0x%x", Q_FUNC_INFO, __LINE__, eglGetError()); + EGLint stream_state; + funcs->query_stream(egl_display, state.egl_stream, EGL_STREAM_STATE_KHR, &stream_state); + + if (stream_state == EGL_STREAM_STATE_CREATED_KHR) + if (funcs->stream_consumer_gltexture(egl_display, state.egl_stream) != EGL_TRUE) + qWarning("%s:%d: eglStreamConsumerGLTextureExternalKHR failed: 0x%x", Q_FUNC_INFO, __LINE__, eglGetError()); } else { GLint previousTexture = GL_TEXTURE0; glGetIntegerv(GL_ACTIVE_TEXTURE, &previousTexture); @@ -340,6 +360,11 @@ void WaylandEglClientBufferIntegrationPrivate::handle_buffer_destroy(wl_listener BufferState state = self->buffers.take(buffer); + /* TODO This texture shouldn't get deleted here as the compositor might want to show an transition. But at the same time we need to make sure + that the texture is deleted properly although if the compositor didn't use it (e.g. by creating a quick item)*/ + if (state.eglstream_texture) + glDeleteTextures(1, &state.eglstream_texture); + for (int i = 0; i < state.egl_images.size(); i++) self->egl_destroy_image(self->egl_display, state.egl_images[i]); @@ -453,6 +478,17 @@ QWaylandBufferRef::BufferFormatEgl WaylandEglClientBufferIntegration::bufferForm return formatFromEglFormat(d->buffers.value(buffer).egl_format); } +uint WaylandEglClientBufferIntegration::textureForBuffer(wl_resource *buffer, int plane) +{ + Q_UNUSED(plane) + Q_D(WaylandEglClientBufferIntegration); + if (!buffer) + return 0; + + const BufferState state = d->buffers.value(buffer); + return state.eglstream_texture; +} + void WaylandEglClientBufferIntegration::bindTextureToBuffer(struct ::wl_resource *buffer) { Q_D(WaylandEglClientBufferIntegration); @@ -475,8 +511,15 @@ void WaylandEglClientBufferIntegration::updateTextureForBuffer(struct ::wl_resou const BufferState state = d->buffers.value(buffer); - if (state.egl_stream != EGL_NO_STREAM_KHR) - d->funcs->stream_consumer_acquire(d->egl_display, state.egl_stream); + if (state.egl_stream != EGL_NO_STREAM_KHR) { + EGLint stream_state; + d->funcs->query_stream(d->egl_display, state.egl_stream, EGL_STREAM_STATE_KHR, &stream_state); + + if (stream_state == EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR) { + if (d->funcs->stream_consumer_acquire(d->egl_display, state.egl_stream) != EGL_TRUE) + qWarning("%s:%d: eglStreamConsumerAcquireKHR failed: 0x%x", Q_FUNC_INFO, __LINE__, eglGetError()); + } + } } QWaylandSurface::Origin WaylandEglClientBufferIntegration::origin(struct ::wl_resource *buffer) const diff --git a/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.h b/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.h index 57e83717..74cad708 100644 --- a/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.h +++ b/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.h @@ -54,6 +54,7 @@ public: void initializeBuffer(struct ::wl_resource *buffer) Q_DECL_OVERRIDE; QWaylandBufferRef::BufferFormatEgl bufferFormat(struct ::wl_resource *buffer) Q_DECL_OVERRIDE; + uint textureForBuffer(struct ::wl_resource *buffer, int plane) Q_DECL_OVERRIDE; void bindTextureToBuffer(struct ::wl_resource *buffer) Q_DECL_OVERRIDE; void updateTextureForBuffer(struct ::wl_resource *buffer) Q_DECL_OVERRIDE; |