diff options
author | Louai Al-Khanji <louai.al-khanji@theqtcompany.com> | 2016-02-19 15:58:33 -0800 |
---|---|---|
committer | Louai Al-Khanji <louai.al-khanji@qt.io> | 2016-04-29 16:09:07 +0000 |
commit | ab2d5162039bd7dac6547a5984dbe6e19b7d835c (patch) | |
tree | 6e2f8da3c6e9340634821666c8b7079870f57566 /src | |
parent | aae5a5b19c320f367a7e43d54d84b0562a85737a (diff) |
QtWaylandCompositor: Add support for different EGL buffer formats
Change-Id: Idfeeedbf247fa81dccdf28a1aa04f878900ed262
Reviewed-by: Giulio Camuffo <giulio.camuffo@kdab.com>
Diffstat (limited to 'src')
19 files changed, 680 insertions, 194 deletions
diff --git a/src/compositor/compositor.pro b/src/compositor/compositor.pro index 37f670964..e60506417 100644 --- a/src/compositor/compositor.pro +++ b/src/compositor/compositor.pro @@ -22,6 +22,7 @@ QMAKE_DOCS = $$PWD/doc/qtwaylandcompositor.qdocconf INCLUDEPATH += ../shared HEADERS += ../shared/qwaylandmimehelper.h ../shared/qwaylandinputmethodeventbuilder.h SOURCES += ../shared/qwaylandmimehelper.cpp ../shared/qwaylandinputmethodeventbuilder.cpp +RESOURCES += compositor.qrc include ($$PWD/global/global.pri) include ($$PWD/wayland_wrapper/wayland_wrapper.pri) diff --git a/src/compositor/compositor.qrc b/src/compositor/compositor.qrc new file mode 100644 index 000000000..5dc7a70a5 --- /dev/null +++ b/src/compositor/compositor.qrc @@ -0,0 +1,11 @@ +<RCC> + <qresource prefix="/qt-project.org/wayland/compositor"> + <file>shaders/surface.vert</file> + <file>shaders/surface_oes_external.frag</file> + <file>shaders/surface_rgba.frag</file> + <file>shaders/surface_rgbx.frag</file> + <file>shaders/surface_y_u_v.frag</file> + <file>shaders/surface_y_uv.frag</file> + <file>shaders/surface_y_xuxv.frag</file> + </qresource> +</RCC> diff --git a/src/compositor/compositor_api/qwaylandbufferref.cpp b/src/compositor/compositor_api/qwaylandbufferref.cpp index d0fe7ca40..faff48560 100644 --- a/src/compositor/compositor_api/qwaylandbufferref.cpp +++ b/src/compositor/compositor_api/qwaylandbufferref.cpp @@ -199,6 +199,25 @@ QWaylandSurface::Origin QWaylandBufferRef::origin() const return QWaylandSurface::OriginBottomLeft; } +QWaylandBufferRef::BufferType QWaylandBufferRef::bufferType() const +{ + if (d->nullOrDestroyed()) + return BufferType_Null; + + if (isShm()) + return BufferType_Shm; + + return BufferType_Egl; +} + +QWaylandBufferRef::BufferFormatEgl QWaylandBufferRef::bufferFormatEgl() const +{ + if (d->nullOrDestroyed()) + return BufferFormatEgl_Null; + + return d->buffer->bufferFormatEgl(); +} + /*! * Returns true if the buffer is a shared memory buffer. Otherwise returns false. */ @@ -235,14 +254,6 @@ void QWaylandBufferRef::bindToTexture() const } -int QWaylandBufferRef::textureTarget() const -{ - if (d->nullOrDestroyed()) - return 0x0DE1; // GL_TEXTURE_2D - - return d->buffer->textureTarget(); -} - void QWaylandBufferRef::updateTexture() const { if (d->nullOrDestroyed() || d->buffer->isShm()) diff --git a/src/compositor/compositor_api/qwaylandbufferref.h b/src/compositor/compositor_api/qwaylandbufferref.h index a4a5df5e0..4ded8f17b 100644 --- a/src/compositor/compositor_api/qwaylandbufferref.h +++ b/src/compositor/compositor_api/qwaylandbufferref.h @@ -75,11 +75,29 @@ public: QSize size() const; QWaylandSurface::Origin origin() const; + enum BufferType { + BufferType_Null, + BufferType_Shm, + BufferType_Egl + }; + + enum BufferFormatEgl { + BufferFormatEgl_Null, + BufferFormatEgl_RGB, + BufferFormatEgl_RGBA, + BufferFormatEgl_EXTERNAL_OES, + BufferFormatEgl_Y_U_V, + BufferFormatEgl_Y_UV, + BufferFormatEgl_Y_XUXV + }; + + BufferType bufferType() const; + BufferFormatEgl bufferFormatEgl() const; + bool isShm() const; QImage image() const; void bindToTexture() const; - int textureTarget() const; void updateTexture() const; private: diff --git a/src/compositor/compositor_api/qwaylandquickitem.cpp b/src/compositor/compositor_api/qwaylandquickitem.cpp index ded16a9c3..e46b38ccd 100644 --- a/src/compositor/compositor_api/qwaylandquickitem.cpp +++ b/src/compositor/compositor_api/qwaylandquickitem.cpp @@ -57,8 +57,179 @@ #include <wayland-server.h> #include <QThread> +#ifndef GL_TEXTURE_EXTERNAL_OES +#define GL_TEXTURE_EXTERNAL_OES 0x8D65 +#endif + QT_BEGIN_NAMESPACE +static const struct { + const char * const vertexShaderSourceFile; + const char * const fragmentShaderSourceFile; + GLenum textureTarget; + int planeCount; + bool canProvideTexture; + QSGMaterial::Flags materialFlags; + QSGMaterialType materialType; +} bufferTypes[] = { + // BufferFormatEgl_Null + { "", "", 0, 0, false, 0, {} }, + + // BufferFormatEgl_RGB + { + ":/qt-project.org/wayland/compositor/shaders/surface.vert", + ":/qt-project.org/wayland/compositor/shaders/surface_rgbx.frag", + GL_TEXTURE_2D, 1, true, + QSGMaterial::Blending, + {} + }, + + // BufferFormatEgl_RGBA + { + ":/qt-project.org/wayland/compositor/shaders/surface.vert", + ":/qt-project.org/wayland/compositor/shaders/surface_rgba.frag", + GL_TEXTURE_2D, 1, true, + QSGMaterial::Blending, + {} + }, + + // BufferFormatEgl_EXTERNAL_OES + { + ":/qt-project.org/wayland/compositor/shaders/surface.vert", + ":/qt-project.org/wayland/compositor/shaders/surface_oes_external.frag", + GL_TEXTURE_EXTERNAL_OES, 1, false, + QSGMaterial::Blending, + {} + }, + + // BufferFormatEgl_Y_U_V + { + ":/qt-project.org/wayland/compositor/shaders/surface.vert", + ":/qt-project.org/wayland/compositor/shaders/surface_y_u_v.frag", + GL_TEXTURE_2D, 3, false, + QSGMaterial::Blending, + {} + }, + + // BufferFormatEgl_Y_UV + { + ":/qt-project.org/wayland/compositor/shaders/surface.vert", + ":/qt-project.org/wayland/compositor/shaders/surface_y_uv.frag", + GL_TEXTURE_2D, 2, false, + QSGMaterial::Blending, + {} + }, + + // BufferFormatEgl_Y_XUXV + { + ":/qt-project.org/wayland/compositor/shaders/surface.vert", + ":/qt-project.org/wayland/compositor/shaders/surface_y_xuxv.frag", + GL_TEXTURE_2D, 2, false, + QSGMaterial::Blending, + {} + } +}; + +QWaylandBufferMaterialShader::QWaylandBufferMaterialShader(QWaylandBufferRef::BufferFormatEgl format) + : QSGMaterialShader() + , m_format(format) +{ + setShaderSourceFile(QOpenGLShader::Vertex, QString::fromLatin1(bufferTypes[format].vertexShaderSourceFile)); + setShaderSourceFile(QOpenGLShader::Fragment, QString::fromLatin1(bufferTypes[format].fragmentShaderSourceFile)); +} + +void QWaylandBufferMaterialShader::updateState(const QSGMaterialShader::RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) +{ + QSGMaterialShader::updateState(state, newEffect, oldEffect); + + QWaylandBufferMaterial *material = static_cast<QWaylandBufferMaterial *>(newEffect); + material->bind(); + + if (state.isMatrixDirty()) + program()->setUniformValue(m_id_matrix, state.combinedMatrix()); + + if (state.isOpacityDirty()) + program()->setUniformValue(m_id_opacity, state.opacity()); +} + +const char * const *QWaylandBufferMaterialShader::attributeNames() const +{ + static char const *const attr[] = { "qt_VertexPosition", "qt_VertexTexCoord", 0 }; + return attr; +} + +void QWaylandBufferMaterialShader::initialize() +{ + QSGMaterialShader::initialize(); + + m_id_matrix = program()->uniformLocation("qt_Matrix"); + m_id_opacity = program()->uniformLocation("qt_Opacity"); + + for (int i = 0; i < bufferTypes[m_format].planeCount; i++) { + m_id_tex << program()->uniformLocation("tex" + QByteArray::number(i)); + program()->setUniformValue(m_id_tex[i], i); + } + + Q_ASSERT(m_id_tex.size() == bufferTypes[m_format].planeCount); +} + +QWaylandBufferMaterial::QWaylandBufferMaterial(QWaylandBufferRef::BufferFormatEgl format) + : QSGMaterial() + , m_format(format) +{ + 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); +} + +QWaylandBufferMaterial::~QWaylandBufferMaterial() +{ + QOpenGLFunctions *gl = QOpenGLContext::currentContext()->functions(); + + for (GLuint texture : m_textures) + gl->glDeleteTextures(1, &texture); +} + +void QWaylandBufferMaterial::bind() +{ + QOpenGLFunctions *gl = QOpenGLContext::currentContext()->functions(); + const GLenum target = bufferTypes[m_format].textureTarget; + + switch (m_textures.size()) { + case 3: + gl->glActiveTexture(GL_TEXTURE2); + gl->glBindTexture(target, m_textures[2]); + case 2: + gl->glActiveTexture(GL_TEXTURE1); + gl->glBindTexture(target, m_textures[1]); + case 1: + gl->glActiveTexture(GL_TEXTURE0); + gl->glBindTexture(target, m_textures[0]); + } +} + +QSGMaterialType *QWaylandBufferMaterial::type() const +{ + return const_cast<QSGMaterialType *>(&bufferTypes[m_format].materialType); +} + +QSGMaterialShader *QWaylandBufferMaterial::createShader() const +{ + return new QWaylandBufferMaterialShader(m_format); +} + QMutex *QWaylandQuickItemPrivate::mutex = 0; class QWaylandSurfaceTextureProvider : public QSGTextureProvider @@ -85,7 +256,6 @@ public: if (m_ref.hasBuffer()) { if (buffer.isShm()) { m_sgTex = surfaceItem->window()->createTextureFromImage(buffer.image()); - m_invertY = false; if (m_sgTex) { m_sgTex->bind(); } @@ -101,7 +271,6 @@ public: glBindTexture(GL_TEXTURE_2D, texture); buffer.bindToTexture(); m_sgTex = surfaceItem->window()->createTextureFromId(texture , QSize(surfaceItem->width(), surfaceItem->height()), opt); - m_invertY = buffer.origin() == QWaylandSurface::OriginBottomLeft; } } emit textureChanged(); @@ -115,10 +284,8 @@ public: } void setSmooth(bool smooth) { m_smooth = smooth; } - bool invertY() const { return m_invertY; } private: bool m_smooth; - bool m_invertY; QSGTexture *m_sgTex; QWaylandBufferRef m_ref; }; @@ -249,6 +416,12 @@ QWaylandSurface::Origin QWaylandQuickItem::origin() const return d->origin; } +bool QWaylandQuickItem::isTextureProvider() const +{ + Q_D(const QWaylandQuickItem); + return QQuickItem::isTextureProvider() || d->provider; +} + /*! * Returns the texture provider of this QWaylandQuickItem. */ @@ -822,37 +995,73 @@ void QWaylandQuickItem::updateInputMethod(Qt::InputMethodQueries queries) QSGNode *QWaylandQuickItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) { Q_D(QWaylandQuickItem); - bool mapped = (surface() && surface()->isMapped() && d->view->currentBuffer().hasBuffer()) - || (d->view->isBufferLocked() && d->provider); + const bool mapped = surface() && surface()->isMapped() && d->view->currentBuffer().hasBuffer(); if (!mapped || !d->paintEnabled) { delete oldNode; return 0; } - QSGSimpleTextureNode *node = static_cast<QSGSimpleTextureNode *>(oldNode); + QWaylandBufferRef ref = d->view->currentBuffer(); + const bool invertY = ref.origin() == QWaylandSurface::OriginBottomLeft; + const QRectF rect = invertY ? QRectF(0, height(), width(), -height()) + : QRectF(0, 0, width(), height()); - if (!node) - node = new QSGSimpleTextureNode(); + if (ref.isShm() || bufferTypes[ref.bufferFormatEgl()].canProvideTexture) { + QSGSimpleTextureNode *node = static_cast<QSGSimpleTextureNode *>(oldNode); - if (!d->provider) - d->provider = new QWaylandSurfaceTextureProvider(); + if (!node) + node = new QSGSimpleTextureNode(); - if (d->newTexture) { - d->newTexture = false; - d->provider->setBufferRef(this, d->view->currentBuffer()); - node->setTexture(d->provider->texture()); - } + if (!d->provider) + d->provider = new QWaylandSurfaceTextureProvider(); + + if (d->newTexture) { + d->newTexture = false; + d->provider->setBufferRef(this, ref); + node->setTexture(d->provider->texture()); + } - d->provider->setSmooth(smooth()); + d->provider->setSmooth(smooth()); + node->setRect(rect); - if (d->provider->invertY()) { - node->setRect(0, height(), width(), -height()); + return node; } else { - node->setRect(0, 0, width(), height()); + Q_ASSERT(!d->provider); + + QSGGeometryNode *node = static_cast<QSGGeometryNode *>(oldNode); + + if (!node) + node = new QSGGeometryNode; + + QSGGeometry *geometry = node->geometry(); + QWaylandBufferMaterial *material = static_cast<QWaylandBufferMaterial *>(node->material()); + + if (!geometry) + geometry = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4); + + if (!material) + material = new QWaylandBufferMaterial(ref.bufferFormatEgl()); + + if (d->newTexture) { + d->newTexture = false; + material->bind(); + ref.bindToTexture(); + } + + ref.updateTexture(); + QSGGeometry::updateTexturedRectGeometry(geometry, rect, QRectF(0, 0, 1, 1)); + + node->setGeometry(geometry); + node->setFlag(QSGNode::OwnsGeometry, true); + + node->setMaterial(material); + node->setFlag(QSGNode::OwnsMaterial, true); + + return node; } - return node; + Q_UNREACHABLE(); } void QWaylandQuickItem::setTouchEventsEnabled(bool enabled) @@ -909,3 +1118,4 @@ void QWaylandQuickItem::handleSubsurfacePosition(const QPoint &pos) } QT_END_NAMESPACE + diff --git a/src/compositor/compositor_api/qwaylandquickitem.h b/src/compositor/compositor_api/qwaylandquickitem.h index 20b28b5f0..08827981a 100644 --- a/src/compositor/compositor_api/qwaylandquickitem.h +++ b/src/compositor/compositor_api/qwaylandquickitem.h @@ -80,7 +80,7 @@ public: QWaylandSurface::Origin origin() const; - bool isTextureProvider() const Q_DECL_OVERRIDE { return true; } + bool isTextureProvider() const Q_DECL_OVERRIDE; QSGTextureProvider *textureProvider() const Q_DECL_OVERRIDE; bool paintEnabled() const; diff --git a/src/compositor/compositor_api/qwaylandquickitem_p.h b/src/compositor/compositor_api/qwaylandquickitem_p.h index eab99f8f3..5c953771e 100644 --- a/src/compositor/compositor_api/qwaylandquickitem_p.h +++ b/src/compositor/compositor_api/qwaylandquickitem_p.h @@ -49,6 +49,8 @@ // #include <QtQuick/private/qquickitem_p.h> +#include <QtQuick/QSGMaterialShader> +#include <QtQuick/QSGMaterial> #include "qwaylandquickitem.h" @@ -59,6 +61,40 @@ QT_BEGIN_NAMESPACE class QWaylandSurfaceTextureProvider; class QMutex; +class QWaylandBufferMaterialShader : public QSGMaterialShader +{ +public: + QWaylandBufferMaterialShader(QWaylandBufferRef::BufferFormatEgl format); + + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) Q_DECL_OVERRIDE; + char const *const *attributeNames() const Q_DECL_OVERRIDE; + +protected: + void initialize() Q_DECL_OVERRIDE; + +private: + const QWaylandBufferRef::BufferFormatEgl m_format; + int m_id_matrix; + int m_id_opacity; + QVarLengthArray<int, 3> m_id_tex; +}; + +class QWaylandBufferMaterial : public QSGMaterial +{ +public: + QWaylandBufferMaterial(QWaylandBufferRef::BufferFormatEgl format); + ~QWaylandBufferMaterial(); + + void bind(); + + QSGMaterialType *type() const Q_DECL_OVERRIDE; + QSGMaterialShader *createShader() const Q_DECL_OVERRIDE; + +private: + const QWaylandBufferRef::BufferFormatEgl m_format; + QVarLengthArray<GLuint, 3> m_textures; +}; + class QWaylandQuickItemPrivate : public QQuickItemPrivate { Q_DECLARE_PUBLIC(QWaylandQuickItem) diff --git a/src/compositor/hardware_integration/qwlclientbufferintegration_p.h b/src/compositor/hardware_integration/qwlclientbufferintegration_p.h index 2dff20e8f..1fbcb7991 100644 --- a/src/compositor/hardware_integration/qwlclientbufferintegration_p.h +++ b/src/compositor/hardware_integration/qwlclientbufferintegration_p.h @@ -50,6 +50,7 @@ #include <QtWaylandCompositor/qwaylandexport.h> #include <QtWaylandCompositor/qwaylandsurface.h> +#include <QtWaylandCompositor/qwaylandbufferref.h> #include <QtCore/QSize> #include <wayland-server.h> @@ -71,7 +72,7 @@ public: virtual void initializeHardware(struct ::wl_display *display) = 0; virtual void initializeBuffer(struct ::wl_resource *buffer) { Q_UNUSED(buffer); } - virtual int textureTargetForBuffer(struct ::wl_resource *buffer) const { Q_UNUSED(buffer); return 0x0DE1; } + virtual QWaylandBufferRef::BufferFormatEgl bufferFormat(struct ::wl_resource *buffer) { Q_UNUSED(buffer); return QWaylandBufferRef::BufferFormatEgl_RGBA; } virtual void bindTextureToBuffer(struct ::wl_resource *buffer) = 0; virtual void updateTextureForBuffer(struct ::wl_resource *buffer) { Q_UNUSED(buffer); } diff --git a/src/compositor/shaders/surface.vert b/src/compositor/shaders/surface.vert new file mode 100644 index 000000000..848b334f3 --- /dev/null +++ b/src/compositor/shaders/surface.vert @@ -0,0 +1,9 @@ +uniform highp mat4 qt_Matrix; +attribute highp vec2 qt_VertexPosition; +attribute highp vec2 qt_VertexTexCoord; +varying highp vec2 v_texcoord; + +void main() { + gl_Position = qt_Matrix * vec4(qt_VertexPosition, 0.0, 1.0); + v_texcoord = qt_VertexTexCoord; +} diff --git a/src/compositor/shaders/surface_oes_external.frag b/src/compositor/shaders/surface_oes_external.frag new file mode 100644 index 000000000..724d06a85 --- /dev/null +++ b/src/compositor/shaders/surface_oes_external.frag @@ -0,0 +1,8 @@ +#extension GL_OES_EGL_image_external : require +varying highp vec2 v_texcoord; +uniform highp samplerExternalOES tex0; +uniform lowp float qt_Opacity; + +void main() { + gl_FragColor = qt_Opacity * texture2D(tex0, v_texcoord); +} diff --git a/src/compositor/shaders/surface_rgba.frag b/src/compositor/shaders/surface_rgba.frag new file mode 100644 index 000000000..f896051ba --- /dev/null +++ b/src/compositor/shaders/surface_rgba.frag @@ -0,0 +1,7 @@ +varying highp vec2 v_texcoord; +uniform highp sampler2D tex0; +uniform lowp float qt_Opacity; + +void main() { + gl_FragColor = qt_Opacity * texture2D(tex0, v_texcoord); +} diff --git a/src/compositor/shaders/surface_rgbx.frag b/src/compositor/shaders/surface_rgbx.frag new file mode 100644 index 000000000..8fb78498c --- /dev/null +++ b/src/compositor/shaders/surface_rgbx.frag @@ -0,0 +1,8 @@ +varying highp vec2 v_texcoord; +uniform highp sampler2D tex0; +uniform lowp float qt_Opacity; + +void main() { + gl_FragColor.rgb = qt_Opacity * texture2D(tex0, v_texcoord).rgb; + gl_FragColor.a = qt_Opacity; +} diff --git a/src/compositor/shaders/surface_y_u_v.frag b/src/compositor/shaders/surface_y_u_v.frag new file mode 100644 index 000000000..e739f6fff --- /dev/null +++ b/src/compositor/shaders/surface_y_u_v.frag @@ -0,0 +1,18 @@ +uniform highp sampler2D tex0; +uniform highp sampler2D tex1; +uniform highp sampler2D tex2; +varying highp vec2 v_texcoord; +uniform lowp float qt_Opacity; + +void main() { + float y = 1.16438356 * (texture2D(tex0, v_texcoord).x - 0.0625); + float u = texture2D(tex1, v_texcoord).x - 0.5; + float v = texture2D(tex2, v_texcoord).x - 0.5; + y *= qt_Opacity; + u *= qt_Opacity; + v *= qt_Opacity; + gl_FragColor.r = y + 1.59602678 * v; + gl_FragColor.g = y - 0.39176229 * u - 0.81296764 * v; + gl_FragColor.b = y + 2.01723214 * u; + gl_FragColor.a = qt_Opacity; +} diff --git a/src/compositor/shaders/surface_y_uv.frag b/src/compositor/shaders/surface_y_uv.frag new file mode 100644 index 000000000..e3fbcdf8d --- /dev/null +++ b/src/compositor/shaders/surface_y_uv.frag @@ -0,0 +1,17 @@ +uniform highp sampler2D tex0; +uniform highp sampler2D tex1; +varying highp vec2 v_texcoord; +uniform lowp float qt_Opacity; + +void main() { + float y = 1.16438356 * (texture2D(tex0, v_texcoord).x - 0.0625); + float u = texture2D(tex1, v_texcoord).r - 0.5; + float v = texture2D(tex1, v_texcoord).g - 0.5; + y *= qt_Opacity; + u *= qt_Opacity; + v *= qt_Opacity; + gl_FragColor.r = y + 1.59602678 * v; + gl_FragColor.g = y - 0.39176229 * u - 0.81296764 * v; + gl_FragColor.b = y + 2.01723214 * u; + gl_FragColor.a = qt_Opacity; +} diff --git a/src/compositor/shaders/surface_y_xuxv.frag b/src/compositor/shaders/surface_y_xuxv.frag new file mode 100644 index 000000000..79f8600e8 --- /dev/null +++ b/src/compositor/shaders/surface_y_xuxv.frag @@ -0,0 +1,17 @@ +uniform highp sampler2D tex0; +uniform highp sampler2D tex1; +varying highp vec2 v_texcoord; +uniform lowp float qt_Opacity; + +void main() { + float y = 1.16438356 * (texture2D(tex0, v_texcoord).x - 0.0625); + float u = texture2D(tex1, v_texcoord).g - 0.5; + float v = texture2D(tex1, v_texcoord).a - 0.5; + y *= qt_Opacity; + u *= qt_Opacity; + v *= qt_Opacity; + gl_FragColor.r = y + 1.59602678 * v; + gl_FragColor.g = y - 0.39176229 * u - 0.81296764 * v; + gl_FragColor.b = y + 2.01723214 * u; + gl_FragColor.a = qt_Opacity; +} diff --git a/src/compositor/wayland_wrapper/qwlsurfacebuffer.cpp b/src/compositor/wayland_wrapper/qwlsurfacebuffer.cpp index f48dd3400..a6509b030 100644 --- a/src/compositor/wayland_wrapper/qwlsurfacebuffer.cpp +++ b/src/compositor/wayland_wrapper/qwlsurfacebuffer.cpp @@ -198,6 +198,16 @@ QImage SurfaceBuffer::image() const return QImage(); } +QWaylandBufferRef::BufferFormatEgl SurfaceBuffer::bufferFormatEgl() const +{ + Q_ASSERT(isShm() == false); + + if (QtWayland::ClientBufferIntegration *clientInt = QWaylandCompositorPrivate::get(m_compositor)->clientBufferIntegration()) + return clientInt->bufferFormat(m_buffer); + + return QWaylandBufferRef::BufferFormatEgl_Null; +} + void SurfaceBuffer::bindToTexture() const { Q_ASSERT(m_compositor); @@ -221,14 +231,6 @@ void SurfaceBuffer::bindToTexture() const } } -int SurfaceBuffer::textureTarget() const -{ - if (QtWayland::ClientBufferIntegration *clientInt = QWaylandCompositorPrivate::get(m_compositor)->clientBufferIntegration()) - return clientInt->textureTargetForBuffer(m_buffer); - - 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 1d186e2d8..85f78d41b 100644 --- a/src/compositor/wayland_wrapper/qwlsurfacebuffer_p.h +++ b/src/compositor/wayland_wrapper/qwlsurfacebuffer_p.h @@ -54,6 +54,7 @@ #include <QAtomicInt> #include <QtWaylandCompositor/QWaylandSurface> +#include <QtWaylandCompositor/QWaylandBufferRef> #include <wayland-server.h> @@ -103,7 +104,7 @@ public: bool isShm() const { return wl_shm_buffer_get(m_buffer); } QImage image() const; - int textureTarget() const; + QWaylandBufferRef::BufferFormatEgl bufferFormatEgl() const; void bindToTexture() const; void updateTexture() const; diff --git a/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.cpp b/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.cpp index d573edfcb..e6b68225b 100644 --- a/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.cpp +++ b/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.cpp @@ -51,11 +51,43 @@ #include <QtPlatformSupport/private/qeglstreamconvenience_p.h> #ifndef GL_TEXTURE_EXTERNAL_OES -#define GL_TEXTURE_EXTERNAL_OES 0x8D65 +#define GL_TEXTURE_EXTERNAL_OES 0x8D65 #endif #ifndef EGL_WAYLAND_BUFFER_WL -#define EGL_WAYLAND_BUFFER_WL 0x31D5 +#define EGL_WAYLAND_BUFFER_WL 0x31D5 +#endif + +#ifndef EGL_WAYLAND_PLANE_WL +#define EGL_WAYLAND_PLANE_WL 0x31D6 +#endif + +#ifndef EGL_WAYLAND_Y_INVERTED_WL +#define EGL_WAYLAND_Y_INVERTED_WL 0x31DB +#endif + +#ifndef EGL_TEXTURE_RGB +#define EGL_TEXTURE_RGB 0x305D +#endif + +#ifndef EGL_TEXTURE_RGBA +#define EGL_TEXTURE_RGBA 0x305E +#endif + +#ifndef EGL_TEXTURE_EXTERNAL_WL +#define EGL_TEXTURE_EXTERNAL_WL 0x31DA +#endif + +#ifndef EGL_TEXTURE_Y_U_V_WL +#define EGL_TEXTURE_Y_U_V_WL 0x31D7 +#endif + +#ifndef EGL_TEXTURE_Y_UV_WL +#define EGL_TEXTURE_Y_UV_WL 0x31D8 +#endif + +#ifndef EGL_TEXTURE_Y_XUXV_WL +#define EGL_TEXTURE_Y_XUXV_WL 0x31D9 #endif /* Needed for compatibility with Mesa older than 10.0. */ @@ -80,93 +112,240 @@ QT_BEGIN_NAMESPACE struct BufferState { - BufferState() - : gl_texture(0) - , gl_texture_target(GL_TEXTURE_2D) - , egl_stream(EGL_NO_STREAM_KHR) - , isYInverted(true) - {} - - GLuint gl_texture; - GLenum gl_texture_target; + BufferState(); + + EGLint egl_format; + QVarLengthArray<EGLImageKHR, 3> egl_images; EGLStreamKHR egl_stream; + bool isYInverted; QSize size; }; -struct buffer_destroy_listener +class WaylandEglClientBufferIntegrationPrivate { - struct wl_listener listener; - class WaylandEglClientBufferIntegrationPrivate *d; +public: + WaylandEglClientBufferIntegrationPrivate(); + + void attach(struct ::wl_resource *buffer); + void attach_egl_texture(struct ::wl_resource *buffer, EGLint format); + void attach_egl_fd_texture(struct ::wl_resource *buffer, EGLNativeFileDescriptorKHR streamFd); + void register_buffer(struct ::wl_resource *buffer, BufferState state); + void bindBuffer(struct ::wl_resource *buffer); + + static void handle_buffer_destroy(wl_listener *listener, void *data); + + EGLDisplay egl_display; + bool valid; + bool display_bound; + QHash<struct ::wl_resource *, BufferState> buffers; + + PFNEGLBINDWAYLANDDISPLAYWL egl_bind_wayland_display; + PFNEGLUNBINDWAYLANDDISPLAYWL egl_unbind_wayland_display; + PFNEGLQUERYWAYLANDBUFFERWL_compat egl_query_wayland_buffer; + + PFNEGLCREATEIMAGEKHRPROC egl_create_image; + PFNEGLDESTROYIMAGEKHRPROC egl_destroy_image; + + PFNGLEGLIMAGETARGETTEXTURE2DOESPROC gl_egl_image_target_texture_2d; + + QEGLStreamConvenience *funcs; }; -class WaylandEglClientBufferIntegrationPrivate +struct buffer_destroy_listener : wl_listener { -public: - WaylandEglClientBufferIntegrationPrivate() - : egl_display(EGL_NO_DISPLAY) - , valid(false) - , display_bound(false) - , egl_bind_wayland_display(0) - , egl_unbind_wayland_display(0) - , egl_query_wayland_buffer(0) - , egl_create_image(0) - , egl_destroy_image(0) - , gl_egl_image_target_texture_2d(0) - , funcs(Q_NULLPTR) + buffer_destroy_listener() + : d(0) { + notify = WaylandEglClientBufferIntegrationPrivate::handle_buffer_destroy; + wl_list_init(&this->link); } - static void destroy_listener_callback(wl_listener *listener, void *data) { - static QMutex mutex; - QMutexLocker locker(&mutex); + WaylandEglClientBufferIntegrationPrivate *d; +}; - buffer_destroy_listener *destroy_listener = reinterpret_cast<buffer_destroy_listener *>(listener); - WaylandEglClientBufferIntegrationPrivate *self = destroy_listener->d; - struct ::wl_resource *buffer = static_cast<struct ::wl_resource *>(data); +BufferState::BufferState() + : egl_format(EGL_TEXTURE_RGBA) + , egl_stream(EGL_NO_STREAM_KHR) + , isYInverted(true) +{} + +WaylandEglClientBufferIntegrationPrivate::WaylandEglClientBufferIntegrationPrivate() + : egl_display(EGL_NO_DISPLAY) + , valid(false) + , display_bound(false) + , egl_bind_wayland_display(0) + , egl_unbind_wayland_display(0) + , egl_query_wayland_buffer(0) + , egl_create_image(0) + , egl_destroy_image(0) + , gl_egl_image_target_texture_2d(0) + , funcs(Q_NULLPTR) +{} + +void WaylandEglClientBufferIntegrationPrivate::attach(struct ::wl_resource *buffer) +{ + EGLint format; + EGLNativeFileDescriptorKHR streamFd = EGL_NO_FILE_DESCRIPTOR_KHR; - wl_list_remove(&listener->link); - delete listener; + if (egl_query_wayland_buffer(egl_display, buffer, EGL_TEXTURE_FORMAT, &format)) + attach_egl_texture(buffer, format); + else if (egl_query_wayland_buffer(egl_display, buffer, EGL_WAYLAND_BUFFER_WL, &streamFd)) + attach_egl_fd_texture(buffer, streamFd); +} - if (!self->buffers.contains(buffer)) - return; +void WaylandEglClientBufferIntegrationPrivate::attach_egl_texture(struct ::wl_resource *buffer, EGLint format) +{ + // Resolving GL functions may need a context current, so do it only here. + if (!gl_egl_image_target_texture_2d) + gl_egl_image_target_texture_2d = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress("glEGLImageTargetTexture2DOES")); - Q_ASSERT(self); - Q_ASSERT(buffer); + if (!gl_egl_image_target_texture_2d) { + qWarning("QtCompositor: bindTextureToBuffer() failed. Could not find glEGLImageTargetTexture2DOES."); + return; + } - BufferState state = self->buffers.take(buffer); + BufferState state; + state.egl_format = format; - if (state.gl_texture != 0) - glDeleteTextures(1, &state.gl_texture); +#if defined(EGL_WAYLAND_Y_INVERTED_WL) + EGLint isYInverted; + EGLBoolean ret = egl_query_wayland_buffer(egl_display, buffer, EGL_WAYLAND_Y_INVERTED_WL, &isYInverted); + // Yes, this looks strange, but the specification says that EGL_FALSE return + // value (not supported) should be treated the same as EGL_TRUE return value + // and EGL_TRUE in value. + state.isYInverted = (ret == EGL_FALSE || isYInverted == EGL_TRUE); +#endif - if (state.egl_stream != EGL_NO_STREAM_KHR) - self->funcs->destroy_stream(self->egl_display, state.egl_stream); + int planes = 1; + + switch (format) { + default: + case EGL_TEXTURE_RGB: + case EGL_TEXTURE_RGBA: + case EGL_TEXTURE_EXTERNAL_WL: + planes = 1; + break; + case EGL_TEXTURE_Y_UV_WL: + planes = 2; + break; + case EGL_TEXTURE_Y_U_V_WL: + planes = 3; + break; + case EGL_TEXTURE_Y_XUXV_WL: + planes = 2; + break; } - void create_destroy_listener(struct ::wl_resource *buffer) { - buffer_destroy_listener *newListener = new buffer_destroy_listener; - newListener->d = this; - newListener->listener.notify = destroy_listener_callback; + for (int i = 0; i < planes; i++) { + const EGLint attribs[] = { EGL_WAYLAND_PLANE_WL, i, EGL_NONE }; + EGLImageKHR image = egl_create_image(egl_display, + EGL_NO_CONTEXT, + EGL_WAYLAND_BUFFER_WL, + buffer, + attribs); + + if (image == EGL_NO_IMAGE_KHR) + qWarning("failed to create EGL image for plane %d", i); - wl_signal_add(&buffer->destroy_signal, &newListener->listener); + state.egl_images << image; } - EGLDisplay egl_display; - bool valid; - bool display_bound; - QHash<struct ::wl_resource *, BufferState> buffers; + register_buffer(buffer, state); +} - PFNEGLBINDWAYLANDDISPLAYWL egl_bind_wayland_display; - PFNEGLUNBINDWAYLANDDISPLAYWL egl_unbind_wayland_display; - PFNEGLQUERYWAYLANDBUFFERWL_compat egl_query_wayland_buffer; +void WaylandEglClientBufferIntegrationPrivate::attach_egl_fd_texture(struct ::wl_resource *buffer, EGLNativeFileDescriptorKHR streamFd) +{ + BufferState state; - PFNEGLCREATEIMAGEKHRPROC egl_create_image; - PFNEGLDESTROYIMAGEKHRPROC egl_destroy_image; + state.egl_format = EGL_TEXTURE_EXTERNAL_WL; + state.isYInverted = false; + state.egl_stream = funcs->create_stream_from_file_descriptor(egl_display, streamFd); - PFNGLEGLIMAGETARGETTEXTURE2DOESPROC gl_egl_image_target_texture_2d; + close(streamFd); - QEGLStreamConvenience *funcs; -}; + if (state.egl_stream == EGL_NO_STREAM_KHR) { + qWarning("%s:%d: eglCreateStreamFromFileDescriptorKHR failed: 0x%x", Q_FUNC_INFO, __LINE__, eglGetError()); + return; + } + + register_buffer(buffer, state); +} + +void WaylandEglClientBufferIntegrationPrivate::register_buffer(struct ::wl_resource *buffer, BufferState state) +{ + Q_ASSERT(!buffers.contains(buffer)); + + EGLint width, height; + egl_query_wayland_buffer(egl_display, buffer, EGL_WIDTH, &width); + egl_query_wayland_buffer(egl_display, buffer, EGL_HEIGHT, &height); + state.size = QSize(width, height); + + buffers[buffer] = state; + + buffer_destroy_listener *destroy_listener = new buffer_destroy_listener; + destroy_listener->d = this; + wl_signal_add(&buffer->destroy_signal, destroy_listener); +} + +void WaylandEglClientBufferIntegrationPrivate::bindBuffer(struct ::wl_resource *buffer) +{ + if (!valid) { + qWarning("QtCompositor: bindTextureToBuffer() failed"); + return; + } + + if (!buffer || !buffers.contains(buffer)) + return; + + 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()); + } else { + GLint previousTexture = GL_TEXTURE0; + glGetIntegerv(GL_ACTIVE_TEXTURE, &previousTexture); + + const GLenum target = (state.egl_format == EGL_TEXTURE_EXTERNAL_WL) ? GL_TEXTURE_EXTERNAL_OES + : GL_TEXTURE_2D; + + for (int i = 0; i < state.egl_images.size(); i++) { + glActiveTexture(GL_TEXTURE0 + i); + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl_egl_image_target_texture_2d(target, state.egl_images[i]); + } + + glActiveTexture(previousTexture); + } +} + +void WaylandEglClientBufferIntegrationPrivate::handle_buffer_destroy(wl_listener *listener, void *data) +{ + buffer_destroy_listener *destroy_listener = static_cast<buffer_destroy_listener *>(listener); + WaylandEglClientBufferIntegrationPrivate *self = destroy_listener->d; + struct ::wl_resource *buffer = static_cast<struct ::wl_resource *>(data); + + wl_list_remove(&destroy_listener->link); + delete destroy_listener; + + if (!self->buffers.contains(buffer)) + return; + + Q_ASSERT(self); + Q_ASSERT(buffer); + + BufferState state = self->buffers.take(buffer); + + for (int i = 0; i < state.egl_images.size(); i++) + self->egl_destroy_image(self->egl_display, state.egl_images[i]); + + if (state.egl_stream != EGL_NO_STREAM_KHR) + self->funcs->destroy_stream(self->egl_display, state.egl_stream); +} WaylandEglClientBufferIntegration::WaylandEglClientBufferIntegration() : QtWayland::ClientBufferIntegration() @@ -236,24 +415,6 @@ void WaylandEglClientBufferIntegration::initializeHardware(struct wl_display *di d->valid = true; } -static GLuint make_texture(GLenum target) -{ - GLuint texture; - - glGenTextures(1, &texture); - glBindTexture(target, texture); - - return texture; -} - -static void set_texture_params(GLenum target) -{ - glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameterf(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterf(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); -} - void WaylandEglClientBufferIntegration::initializeBuffer(struct ::wl_resource *buffer) { Q_D(WaylandEglClientBufferIntegration); @@ -264,88 +425,38 @@ void WaylandEglClientBufferIntegration::initializeBuffer(struct ::wl_resource *b if (!buffer || d->buffers.contains(buffer)) return; - d->create_destroy_listener(buffer); + d->attach(buffer); +} + +static QWaylandBufferRef::BufferFormatEgl formatFromEglFormat(EGLint format) { + switch (format) { + case EGL_TEXTURE_RGB: + return QWaylandBufferRef::BufferFormatEgl_RGB; + case EGL_TEXTURE_RGBA: + return QWaylandBufferRef::BufferFormatEgl_RGBA; + case EGL_TEXTURE_EXTERNAL_WL: + return QWaylandBufferRef::BufferFormatEgl_EXTERNAL_OES; + case EGL_TEXTURE_Y_UV_WL: + return QWaylandBufferRef::BufferFormatEgl_Y_UV; + case EGL_TEXTURE_Y_U_V_WL: + return QWaylandBufferRef::BufferFormatEgl_Y_U_V; + case EGL_TEXTURE_Y_XUXV_WL: + return QWaylandBufferRef::BufferFormatEgl_Y_XUXV; + } + + return QWaylandBufferRef::BufferFormatEgl_RGBA; } -int WaylandEglClientBufferIntegration::textureTargetForBuffer(struct ::wl_resource *buffer) const +QWaylandBufferRef::BufferFormatEgl WaylandEglClientBufferIntegration::bufferFormat(wl_resource *buffer) { Q_D(const WaylandEglClientBufferIntegration); - - return d->buffers.value(buffer).gl_texture_target; + return formatFromEglFormat(d->buffers.value(buffer).egl_format); } void WaylandEglClientBufferIntegration::bindTextureToBuffer(struct ::wl_resource *buffer) { Q_D(WaylandEglClientBufferIntegration); - if (!d->valid) { - qWarning("QtCompositor: bindTextureToBuffer() failed"); - return; - } - - if (!buffer) - return; - - 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); - } else { - Q_ASSERT(QOpenGLContext::currentContext()); - - EGLint format; - EGLNativeFileDescriptorKHR streamFd = EGL_NO_FILE_DESCRIPTOR_KHR; - - if (d->egl_query_wayland_buffer(d->egl_display, buffer, EGL_TEXTURE_FORMAT, &format)) { - // Resolving GL functions may need a context current, so do it only here. - if (!d->gl_egl_image_target_texture_2d) - d->gl_egl_image_target_texture_2d = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress("glEGLImageTargetTexture2DOES")); - - if (!d->gl_egl_image_target_texture_2d) { - qWarning("QtCompositor: bindTextureToBuffer() failed. Could not find glEGLImageTargetTexture2DOES."); - return; - } - - EGLImageKHR image = d->egl_create_image(d->egl_display, EGL_NO_CONTEXT, - EGL_WAYLAND_BUFFER_WL, - buffer, NULL); - - d->gl_egl_image_target_texture_2d(GL_TEXTURE_2D, image); - set_texture_params(GL_TEXTURE_2D); - d->egl_destroy_image(d->egl_display, image); - } else if (d->egl_query_wayland_buffer(d->egl_display, buffer, EGL_WAYLAND_BUFFER_WL, &streamFd)) { - state.egl_stream = d->funcs->create_stream_from_file_descriptor(d->egl_display, streamFd); - close(streamFd); - - if (state.egl_stream == EGL_NO_STREAM_KHR) { - qWarning("%s:%d: eglCreateStreamFromFileDescriptorKHR failed: 0x%x", Q_FUNC_INFO, __LINE__, eglGetError()); - return; - } - - state.isYInverted = false; - state.gl_texture_target = GL_TEXTURE_EXTERNAL_OES; - state.gl_texture = make_texture(state.gl_texture_target); - set_texture_params(state.gl_texture_target); - - if (d->funcs->stream_consumer_gltexture(d->egl_display, state.egl_stream) != EGL_TRUE) - qWarning("%s:%d: eglStreamConsumerGLTextureExternalKHR failed: 0x%x", Q_FUNC_INFO, __LINE__, eglGetError()); - } - - EGLint width, height; - d->egl_query_wayland_buffer(d->egl_display, buffer, EGL_WIDTH, &width); - d->egl_query_wayland_buffer(d->egl_display, buffer, EGL_HEIGHT, &height); - state.size = QSize(width, height); - -#if defined(EGL_WAYLAND_Y_INVERTED_WL) - EGLint isYInverted; - EGLBoolean ret = d->egl_query_wayland_buffer(d->egl_display, buffer, EGL_WAYLAND_Y_INVERTED_WL, &isYInverted); - // Yes, this looks strange, but the specification says that EGL_FALSE return - // value (not supported) should be treated the same as EGL_TRUE return value - // and EGL_TRUE in value. - state.isYInverted = (ret == EGL_FALSE || isYInverted == EGL_TRUE); -#endif - - d->buffers[buffer] = state; - } + d->bindBuffer(buffer); } // Update is only needed for the EGLStream path as that requires calling acquire diff --git a/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.h b/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.h index 18893645a..57e83717a 100644 --- a/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.h +++ b/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.h @@ -53,7 +53,7 @@ public: void initializeHardware(struct ::wl_display *display) Q_DECL_OVERRIDE; void initializeBuffer(struct ::wl_resource *buffer) Q_DECL_OVERRIDE; - int textureTargetForBuffer(struct ::wl_resource *buffer) const Q_DECL_OVERRIDE; + QWaylandBufferRef::BufferFormatEgl bufferFormat(struct ::wl_resource *buffer) Q_DECL_OVERRIDE; void bindTextureToBuffer(struct ::wl_resource *buffer) Q_DECL_OVERRIDE; void updateTextureForBuffer(struct ::wl_resource *buffer) Q_DECL_OVERRIDE; |