diff --git a/src/backends/x11/standalone/x11_standalone_egl_backend.cpp b/src/backends/x11/standalone/x11_standalone_egl_backend.cpp index 1b76cc9d4e..92bef4f027 100644 --- a/src/backends/x11/standalone/x11_standalone_egl_backend.cpp +++ b/src/backends/x11/standalone/x11_standalone_egl_backend.cpp @@ -433,9 +433,9 @@ EglSurfaceTextureX11::EglSurfaceTextureX11(EglBackend *backend, SurfacePixmapX11 bool EglSurfaceTextureX11::create() { - auto texture = std::make_unique(static_cast(m_backend)); + auto texture = std::make_shared(static_cast(m_backend)); if (texture->create(m_pixmap)) { - m_texture = std::move(texture); + m_texture = {texture}; return true; } else { return false; @@ -445,7 +445,7 @@ bool EglSurfaceTextureX11::create() void EglSurfaceTextureX11::update(const QRegion ®ion) { // mipmaps need to be updated - m_texture->setDirty(); + m_texture.setDirty(); } EglPixmapTexture::EglPixmapTexture(EglBackend *backend) diff --git a/src/backends/x11/standalone/x11_standalone_glx_backend.cpp b/src/backends/x11/standalone/x11_standalone_glx_backend.cpp index d717eae1fe..f6bbd94a19 100644 --- a/src/backends/x11/standalone/x11_standalone_glx_backend.cpp +++ b/src/backends/x11/standalone/x11_standalone_glx_backend.cpp @@ -788,9 +788,9 @@ GlxSurfaceTextureX11::GlxSurfaceTextureX11(GlxBackend *backend, SurfacePixmapX11 bool GlxSurfaceTextureX11::create() { - auto texture = std::make_unique(static_cast(m_backend)); + auto texture = std::make_shared(static_cast(m_backend)); if (texture->create(m_pixmap)) { - m_texture = std::move(texture); + m_texture = {texture}; return true; } else { return false; @@ -800,7 +800,7 @@ bool GlxSurfaceTextureX11::create() void GlxSurfaceTextureX11::update(const QRegion ®ion) { // mipmaps need to be updated - m_texture->setDirty(); + m_texture.setDirty(); } GlxPixmapTexture::GlxPixmapTexture(GlxBackend *backend) diff --git a/src/opengl/glshader.cpp b/src/opengl/glshader.cpp index c0899ac3d9..f87621a234 100644 --- a/src/opengl/glshader.cpp +++ b/src/opengl/glshader.cpp @@ -225,6 +225,8 @@ void GLShader::resolveLocations() m_intLocation[TextureWidth] = uniformLocation("textureWidth"); m_intLocation[TextureHeight] = uniformLocation("textureHeight"); + m_intLocation[Sampler] = uniformLocation("sampler"); + m_intLocation[Sampler1] = uniformLocation("sampler1"); m_intLocation[SourceNamedTransferFunction] = uniformLocation("sourceNamedTransferFunction"); m_intLocation[DestinationNamedTransferFunction] = uniformLocation("destinationNamedTransferFunction"); diff --git a/src/opengl/glshader.h b/src/opengl/glshader.h index 2db75ccb11..840f9b9519 100644 --- a/src/opengl/glshader.h +++ b/src/opengl/glshader.h @@ -109,6 +109,8 @@ public: TextureHeight, SourceNamedTransferFunction, DestinationNamedTransferFunction, + Sampler, + Sampler1, IntUniformCount }; diff --git a/src/opengl/glshadermanager.cpp b/src/opengl/glshadermanager.cpp index 85135b840e..602ffbbbc9 100644 --- a/src/opengl/glshadermanager.cpp +++ b/src/opengl/glshadermanager.cpp @@ -133,6 +133,8 @@ QByteArray ShaderManager::generateFragmentSource(ShaderTraits traits) const if (traits & ShaderTrait::MapTexture) { stream << "uniform sampler2D sampler;\n"; + stream << "uniform sampler2D sampler1;\n"; + stream << "uniform int converter;\n"; stream << varying << " vec2 texcoord0;\n"; } else if (traits & ShaderTrait::MapExternalTexture) { stream << "#extension GL_OES_EGL_image_external : require\n\n"; @@ -208,10 +210,28 @@ QByteArray ShaderManager::generateFragmentSource(ShaderTraits traits) const stream << "\nout vec4 " << output << ";\n"; } + if (traits & ShaderTrait::MapTexture) { + // limited range BT601 in -> full range BT709 out + stream << "vec4 transformY_UV(sampler2D tex0, sampler2D tex1, vec2 texcoord0) {\n"; + stream << " float y = 1.16438356 * (texture2D(tex0, texcoord0).x - 0.0625);\n"; + stream << " float u = texture2D(tex1, texcoord0).r - 0.5;\n"; + stream << " float v = texture2D(tex1, texcoord0).g - 0.5;\n"; + stream << " return vec4(y + 1.59602678 * v" + " , y - 0.39176229 * u - 0.81296764 * v" + " , y + 2.01723214 * u" + " , 1);\n"; + stream << "}\n"; + stream << "\n"; + } + stream << "\nvoid main(void)\n{\n"; stream << " vec4 result;\n"; if (traits & ShaderTrait::MapTexture) { - stream << " result = " << textureLookup << "(sampler, texcoord0);\n"; + stream << " if (converter == 0) {\n"; + stream << " result = " << textureLookup << "(sampler, texcoord0);\n"; + stream << " } else {\n"; + stream << " result = transformY_UV(sampler, sampler1, texcoord0);\n"; + stream << " }\n"; } else if (traits & ShaderTrait::MapExternalTexture) { // external textures require texture2D for sampling stream << " result = texture2D(sampler, texcoord0);\n"; diff --git a/src/platformsupport/scenes/opengl/abstract_egl_backend.cpp b/src/platformsupport/scenes/opengl/abstract_egl_backend.cpp index a8b449e2c0..0f27bce709 100644 --- a/src/platformsupport/scenes/opengl/abstract_egl_backend.cpp +++ b/src/platformsupport/scenes/opengl/abstract_egl_backend.cpp @@ -257,9 +257,32 @@ bool AbstractEglBackend::prefer10bpc() const return false; } +EGLImageKHR AbstractEglBackend::importBufferAsImage(GraphicsBuffer *buffer, int plane, int format, const QSize &size) +{ + std::pair key(buffer, plane); + auto it = m_importedBuffers.constFind(key); + if (Q_LIKELY(it != m_importedBuffers.constEnd())) { + return *it; + } + + Q_ASSERT(buffer->dmabufAttributes()); + EGLImageKHR image = importDmaBufAsImage(*buffer->dmabufAttributes(), plane, format, size); + if (image != EGL_NO_IMAGE_KHR) { + m_importedBuffers[key] = image; + connect(buffer, &QObject::destroyed, this, [this, key]() { + eglDestroyImageKHR(m_display->handle(), m_importedBuffers.take(key)); + }); + } else { + qCWarning(KWIN_OPENGL) << "failed to import dmabuf" << buffer; + } + + return image; +} + EGLImageKHR AbstractEglBackend::importBufferAsImage(GraphicsBuffer *buffer) { - auto it = m_importedBuffers.constFind(buffer); + auto key = std::pair(buffer, 0); + auto it = m_importedBuffers.constFind(key); if (Q_LIKELY(it != m_importedBuffers.constEnd())) { return *it; } @@ -267,10 +290,12 @@ EGLImageKHR AbstractEglBackend::importBufferAsImage(GraphicsBuffer *buffer) Q_ASSERT(buffer->dmabufAttributes()); EGLImageKHR image = importDmaBufAsImage(*buffer->dmabufAttributes()); if (image != EGL_NO_IMAGE_KHR) { - m_importedBuffers[buffer] = image; - connect(buffer, &QObject::destroyed, this, [this, buffer]() { - eglDestroyImageKHR(m_display->handle(), m_importedBuffers.take(buffer)); + m_importedBuffers[key] = image; + connect(buffer, &QObject::destroyed, this, [this, key]() { + eglDestroyImageKHR(m_display->handle(), m_importedBuffers.take(key)); }); + } else { + qCWarning(KWIN_OPENGL) << "failed to import dmabuf" << buffer; } return image; @@ -281,6 +306,11 @@ EGLImageKHR AbstractEglBackend::importDmaBufAsImage(const DmaBufAttributes &dmab return m_display->importDmaBufAsImage(dmabuf); } +EGLImageKHR AbstractEglBackend::importDmaBufAsImage(const DmaBufAttributes &dmabuf, int plane, int format, const QSize &size) const +{ + return m_display->importDmaBufAsImage(dmabuf, plane, format, size); +} + std::shared_ptr AbstractEglBackend::importDmaBufAsTexture(const DmaBufAttributes &attributes) const { return m_context->importDmaBufAsTexture(attributes); diff --git a/src/platformsupport/scenes/opengl/abstract_egl_backend.h b/src/platformsupport/scenes/opengl/abstract_egl_backend.h index 478b26a37e..3feeae21a6 100644 --- a/src/platformsupport/scenes/opengl/abstract_egl_backend.h +++ b/src/platformsupport/scenes/opengl/abstract_egl_backend.h @@ -46,7 +46,9 @@ public: std::shared_ptr importDmaBufAsTexture(const DmaBufAttributes &attributes) const; EGLImageKHR importDmaBufAsImage(const DmaBufAttributes &attributes) const; + EGLImageKHR importDmaBufAsImage(const DmaBufAttributes &attributes, int plane, int format, const QSize &size) const; EGLImageKHR importBufferAsImage(GraphicsBuffer *buffer); + EGLImageKHR importBufferAsImage(GraphicsBuffer *buffer, int plane, int format, const QSize &size); protected: AbstractEglBackend(dev_t deviceId = 0); @@ -72,7 +74,7 @@ protected: QList m_clientExtensions; const dev_t m_deviceId; QList m_tranches; - QHash m_importedBuffers; + QHash, EGLImageKHR> m_importedBuffers; }; } diff --git a/src/platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.cpp b/src/platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.cpp index e1587f81c5..bc741b292b 100644 --- a/src/platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.cpp +++ b/src/platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.cpp @@ -6,11 +6,15 @@ #include "platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.h" #include "core/graphicsbufferview.h" +#include "opengl/glshader.h" +#include "opengl/glshadermanager.h" #include "opengl/gltexture.h" #include "platformsupport/scenes/opengl/abstract_egl_backend.h" #include "utils/common.h" +#include "abstract_egl_backend.h" #include +#include namespace KWin { @@ -63,14 +67,17 @@ bool BasicEGLSurfaceTextureWayland::loadShmTexture(GraphicsBuffer *buffer) return false; } - m_texture = GLTexture::upload(*view.image()); - if (Q_UNLIKELY(!m_texture)) { + std::shared_ptr texture = GLTexture::upload(*view.image()); + if (Q_UNLIKELY(!texture)) { return false; } - m_texture->setFilter(GL_LINEAR); - m_texture->setWrapMode(GL_CLAMP_TO_EDGE); - m_texture->setContentTransform(TextureTransform::MirrorY); + texture->setFilter(GL_LINEAR); + texture->setWrapMode(GL_CLAMP_TO_EDGE); + texture->setContentTransform(TextureTransform::MirrorY); + + m_texture = {{texture}}; + m_bufferType = BufferType::Shm; return true; @@ -90,28 +97,55 @@ void BasicEGLSurfaceTextureWayland::updateShmTexture(GraphicsBuffer *buffer, con } for (const QRect &rect : region) { - m_texture->update(*view.image(), rect.topLeft(), rect); + m_texture.planes[0]->update(*view.image(), rect.topLeft(), rect); } } bool BasicEGLSurfaceTextureWayland::loadDmabufTexture(GraphicsBuffer *buffer) { - EGLImageKHR image = backend()->importBufferAsImage(buffer); - if (Q_UNLIKELY(image == EGL_NO_IMAGE_KHR)) { - qCritical(KWIN_OPENGL) << "Invalid dmabuf-based wl_buffer"; - return false; - } + auto createTexture = [this](EGLImageKHR image, const QSize &size) { + if (Q_UNLIKELY(image == EGL_NO_IMAGE_KHR)) { + qCritical(KWIN_OPENGL) << "Invalid dmabuf-based wl_buffer"; + return std::shared_ptr(); + } - m_texture = std::make_unique(GL_TEXTURE_2D); - m_texture->setSize(buffer->size()); - m_texture->create(); - m_texture->setWrapMode(GL_CLAMP_TO_EDGE); - m_texture->setFilter(GL_LINEAR); - m_texture->bind(); - glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, static_cast(image)); - m_texture->unbind(); - if (m_pixmap->bufferOrigin() == GraphicsBufferOrigin::TopLeft) { - m_texture->setContentTransform(TextureTransform::MirrorY); + auto texture = std::make_shared(GL_TEXTURE_2D); + texture->setSize(size); + texture->create(); + texture->setWrapMode(GL_CLAMP_TO_EDGE); + texture->setFilter(GL_LINEAR); + texture->bind(); + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, static_cast(image)); + texture->unbind(); + if (m_pixmap->bufferOrigin() == GraphicsBufferOrigin::TopLeft) { + texture->setContentTransform(TextureTransform::MirrorY); + } + return texture; + }; + + auto format = formatInfo(buffer->dmabufAttributes()->format); + if (format && format->yuvConversion) { + QList> textures; + Q_ASSERT(format->yuvConversion->plane.count() == uint(buffer->dmabufAttributes()->planeCount)); + for (uint plane = 0; plane < format->yuvConversion->plane.count(); ++plane) { + const auto ¤tPlane = format->yuvConversion->plane[plane]; + QSize size = buffer->size(); + size.rwidth() /= currentPlane.widthDivisor; + size.rheight() /= currentPlane.heightDivisor; + + auto t = createTexture(backend()->importBufferAsImage(buffer, plane, currentPlane.format, size), size); + if (!t) { + return false; + } + textures << t; + } + m_texture = {textures}; + } else { + auto texture = createTexture(backend()->importBufferAsImage(buffer), buffer->size()); + if (!texture) { + return false; + } + m_texture = {{texture}}; } m_bufferType = BufferType::DmaBuf; @@ -126,9 +160,26 @@ void BasicEGLSurfaceTextureWayland::updateDmabufTexture(GraphicsBuffer *buffer) return; } - m_texture->bind(); - glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, static_cast(backend()->importBufferAsImage(buffer))); - m_texture->unbind(); + const GLint target = GL_TEXTURE_2D; + auto format = formatInfo(buffer->dmabufAttributes()->format); + if (format && format->yuvConversion) { + Q_ASSERT(format->yuvConversion->plane.count() == uint(buffer->dmabufAttributes()->planeCount)); + for (uint plane = 0; plane < format->yuvConversion->plane.count(); ++plane) { + const auto ¤tPlane = format->yuvConversion->plane[plane]; + QSize size = buffer->size(); + size.rwidth() /= currentPlane.widthDivisor; + size.rheight() /= currentPlane.heightDivisor; + + m_texture.planes[plane]->bind(); + glEGLImageTargetTexture2DOES(target, static_cast(backend()->importBufferAsImage(buffer, plane, currentPlane.format, size))); + m_texture.planes[plane]->unbind(); + } + } else { + Q_ASSERT(m_texture.planes.count() == 1); + m_texture.planes[0]->bind(); + glEGLImageTargetTexture2DOES(target, static_cast(backend()->importBufferAsImage(buffer))); + m_texture.planes[0]->unbind(); + } } } // namespace KWin diff --git a/src/platformsupport/scenes/opengl/egldisplay.cpp b/src/platformsupport/scenes/opengl/egldisplay.cpp index 646b62627c..1f23e0665f 100644 --- a/src/platformsupport/scenes/opengl/egldisplay.cpp +++ b/src/platformsupport/scenes/opengl/egldisplay.cpp @@ -15,6 +15,7 @@ #include #include +#include #ifndef EGL_DRM_RENDER_NODE_FILE_EXT #define EGL_DRM_RENDER_NODE_FILE_EXT 0x3377 @@ -217,6 +218,29 @@ EGLImageKHR EglDisplay::importDmaBufAsImage(const DmaBufAttributes &dmabuf) cons return eglCreateImageKHR(m_handle, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, attribs.data()); } +EGLImageKHR EglDisplay::importDmaBufAsImage(const DmaBufAttributes &dmabuf, int plane, int format, const QSize &size) const +{ + QList attribs; + attribs.reserve(6 + 1 * 10 + 1); + + attribs << EGL_WIDTH << size.width() + << EGL_HEIGHT << size.height() + << EGL_LINUX_DRM_FOURCC_EXT << format; + + attribs << EGL_DMA_BUF_PLANE0_FD_EXT << dmabuf.fd[plane].get() + << EGL_DMA_BUF_PLANE0_OFFSET_EXT << dmabuf.offset[plane] + << EGL_DMA_BUF_PLANE0_PITCH_EXT << dmabuf.pitch[plane]; + if (dmabuf.modifier != DRM_FORMAT_MOD_INVALID) { + attribs << EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT << EGLint(dmabuf.modifier & 0xffffffff) + << EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT << EGLint(dmabuf.modifier >> 32); + } + attribs << EGL_NONE; + + auto img = eglCreateImageKHR(m_handle, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, attribs.data()); + qDebug() << "retrieving plane" << plane << img; + return img; +} + QHash> EglDisplay::supportedDrmFormats() const { return m_importFormats; diff --git a/src/platformsupport/scenes/opengl/egldisplay.h b/src/platformsupport/scenes/opengl/egldisplay.h index fccf47fade..6d3fd3d9f5 100644 --- a/src/platformsupport/scenes/opengl/egldisplay.h +++ b/src/platformsupport/scenes/opengl/egldisplay.h @@ -13,6 +13,7 @@ #include #include #include +#include #include namespace KWin @@ -43,6 +44,7 @@ public: bool isExternalOnly(uint32_t format, uint64_t modifier) const; EGLImageKHR importDmaBufAsImage(const DmaBufAttributes &dmabuf) const; + EGLImageKHR importDmaBufAsImage(const DmaBufAttributes &dmabuf, int plane, int format, const QSize &size) const; static std::unique_ptr create(::EGLDisplay display, bool owning = true); diff --git a/src/platformsupport/scenes/opengl/openglsurfacetexture.cpp b/src/platformsupport/scenes/opengl/openglsurfacetexture.cpp index 834b9bfc44..457eb2ea3e 100644 --- a/src/platformsupport/scenes/opengl/openglsurfacetexture.cpp +++ b/src/platformsupport/scenes/opengl/openglsurfacetexture.cpp @@ -21,7 +21,7 @@ OpenGLSurfaceTexture::~OpenGLSurfaceTexture() bool OpenGLSurfaceTexture::isValid() const { - return m_texture != nullptr; + return m_texture.isValid(); } OpenGLBackend *OpenGLSurfaceTexture::backend() const @@ -29,9 +29,16 @@ OpenGLBackend *OpenGLSurfaceTexture::backend() const return m_backend; } -GLTexture *OpenGLSurfaceTexture::texture() const +OpenGLSurfaceContents OpenGLSurfaceTexture::texture() const { - return m_texture.get(); + return m_texture; +} + +void OpenGLSurfaceContents::setDirty() +{ + for (auto &plane : planes) { + plane->setDirty(); + } } } // namespace KWin diff --git a/src/platformsupport/scenes/opengl/openglsurfacetexture.h b/src/platformsupport/scenes/opengl/openglsurfacetexture.h index c6cbc8a044..e6815d6554 100644 --- a/src/platformsupport/scenes/opengl/openglsurfacetexture.h +++ b/src/platformsupport/scenes/opengl/openglsurfacetexture.h @@ -11,9 +11,38 @@ namespace KWin { +class GLShader; class GLTexture; class OpenGLBackend; +class KWIN_EXPORT OpenGLSurfaceContents +{ +public: + OpenGLSurfaceContents() + { + } + OpenGLSurfaceContents(const std::shared_ptr &contents) + : planes({contents}) + { + } + OpenGLSurfaceContents(const QList> &planes) + : planes(planes) + { + } + + void setDirty(); + void reset() + { + planes.clear(); + } + bool isValid() const + { + return !planes.isEmpty(); + } + + QList> planes; +}; + class KWIN_EXPORT OpenGLSurfaceTexture : public SurfaceTexture { public: @@ -23,14 +52,14 @@ public: bool isValid() const override; OpenGLBackend *backend() const; - GLTexture *texture() const; + OpenGLSurfaceContents texture() const; virtual bool create() = 0; virtual void update(const QRegion ®ion) = 0; protected: OpenGLBackend *m_backend; - std::unique_ptr m_texture; + OpenGLSurfaceContents m_texture; }; } // namespace KWin diff --git a/src/scene/itemrenderer_opengl.cpp b/src/scene/itemrenderer_opengl.cpp index 2cce9b316d..dbf1a205d3 100644 --- a/src/scene/itemrenderer_opengl.cpp +++ b/src/scene/itemrenderer_opengl.cpp @@ -64,7 +64,7 @@ void ItemRendererOpenGL::setBlendEnabled(bool enabled) m_blendingEnabled = enabled; } -static GLTexture *bindSurfaceTexture(SurfaceItem *surfaceItem) +static OpenGLSurfaceContents bindSurfaceTexture(SurfaceItem *surfaceItem) { SurfacePixmap *surfacePixmap = surfaceItem->pixmap(); auto platformSurfaceTexture = @@ -73,7 +73,7 @@ static GLTexture *bindSurfaceTexture(SurfaceItem *surfaceItem) return platformSurfaceTexture->texture(); } - if (platformSurfaceTexture->texture()) { + if (platformSurfaceTexture->texture().isValid()) { const QRegion region = surfaceItem->damage(); if (!region.isEmpty()) { platformSurfaceTexture->update(region); @@ -81,11 +81,11 @@ static GLTexture *bindSurfaceTexture(SurfaceItem *surfaceItem) } } else { if (!surfacePixmap->isValid()) { - return nullptr; + return {}; } if (!platformSurfaceTexture->create()) { qCDebug(KWIN_OPENGL) << "Failed to bind window"; - return nullptr; + return {}; } surfaceItem->resetDamage(); } @@ -304,14 +304,22 @@ void ItemRendererOpenGL::renderItem(const RenderTarget &renderTarget, const Rend for (int i = 0, v = 0; i < renderContext.renderNodes.count(); i++) { RenderNode &renderNode = renderContext.renderNodes[i]; - if (renderNode.geometry.isEmpty() || !renderNode.texture) { + if (renderNode.geometry.isEmpty() + || (std::holds_alternative(renderNode.texture) && !std::get(renderNode.texture)) + || (std::holds_alternative(renderNode.texture) && !std::get(renderNode.texture).isValid())) { continue; } renderNode.firstVertex = v; renderNode.vertexCount = renderNode.geometry.count(); - renderNode.geometry.postProcessTextureCoordinates(renderNode.texture->matrix(renderNode.coordinateType)); + GLTexture *texture = nullptr; + if (std::holds_alternative(renderNode.texture)) { + texture = std::get(renderNode.texture); + } else { + texture = std::get(renderNode.texture).planes.constFirst().get(); + } + renderNode.geometry.postProcessTextureCoordinates(texture->matrix(renderNode.coordinateType)); renderNode.geometry.copy(map->subspan(v)); v += renderNode.geometry.count(); @@ -361,6 +369,11 @@ void ItemRendererOpenGL::renderItem(const RenderTarget &renderTarget, const Rend shader->setUniform(GLShader::Saturation, data.saturation()); shader->setUniform(GLShader::Vec3Uniform::PrimaryBrightness, QVector3D(toXYZ(1, 0), toXYZ(1, 1), toXYZ(1, 2))); } + + if (traits & ShaderTrait::MapTexture) { + shader->setUniform(GLShader::Sampler, 0); + shader->setUniform(GLShader::Sampler1, 1); + } } shader->setUniform(GLShader::ModelViewProjectionMatrix, renderContext.projectionMatrix * renderNode.transformMatrix); if (traits & ShaderTrait::Modulate) { @@ -370,10 +383,34 @@ void ItemRendererOpenGL::renderItem(const RenderTarget &renderTarget, const Rend shader->setColorspaceUniforms(renderNode.colorDescription, renderTarget.colorDescription()); } - renderNode.texture->bind(); + if (std::holds_alternative(renderNode.texture)) { + const auto texture = std::get(renderNode.texture); + glActiveTexture(GL_TEXTURE0); + shader->setUniform("converter", 0); + texture->bind(); + } else { + const auto contents = std::get(renderNode.texture); + shader->setUniform("converter", contents.planes.count() > 1); + for (int plane = 0; plane < contents.planes.count(); ++plane) { + glActiveTexture(GL_TEXTURE0 + plane); + contents.planes[plane]->bind(); + } + } vbo->draw(scissorRegion, GL_TRIANGLES, renderNode.firstVertex, renderNode.vertexCount, renderContext.hardwareClipping); + + if (std::holds_alternative(renderNode.texture)) { + auto texture = std::get(renderNode.texture); + glActiveTexture(GL_TEXTURE0); + texture->unbind(); + } else { + const auto contents = std::get(renderNode.texture); + for (int plane = 0; plane < contents.planes.count(); ++plane) { + glActiveTexture(GL_TEXTURE0 + plane); + contents.planes[plane]->unbind(); + } + } } if (shader) { ShaderManager::instance()->popShader(); @@ -421,7 +458,16 @@ void ItemRendererOpenGL::visualizeFractional(const RenderViewport &viewport, con setBlendEnabled(true); - m_debug.fractionalShader->setUniform("geometrySize", QVector2D(renderNode.texture->width(), renderNode.texture->height())); + QVector2D size; + if (std::holds_alternative(renderNode.texture)) { + auto texture = std::get(renderNode.texture); + size = QVector2D(texture->width(), texture->height()); + } else { + auto texture = std::get(renderNode.texture).planes.constFirst().get(); + size = QVector2D(texture->width(), texture->height()); + } + + m_debug.fractionalShader->setUniform("geometrySize", size); m_debug.fractionalShader->setUniform(GLShader::ModelViewProjectionMatrix, renderContext.projectionMatrix * renderNode.transformMatrix); vbo->draw(region, GL_TRIANGLES, renderNode.firstVertex, diff --git a/src/scene/itemrenderer_opengl.h b/src/scene/itemrenderer_opengl.h index 9d188da738..186764da98 100644 --- a/src/scene/itemrenderer_opengl.h +++ b/src/scene/itemrenderer_opengl.h @@ -8,6 +8,7 @@ #include "libkwineffects/kwineffects.h" #include "opengl/glutils.h" +#include "platformsupport/scenes/opengl/openglsurfacetexture.h" #include "scene/itemrenderer.h" namespace KWin @@ -18,7 +19,7 @@ class KWIN_EXPORT ItemRendererOpenGL : public ItemRenderer public: struct RenderNode { - GLTexture *texture = nullptr; + std::variant texture; RenderGeometry geometry; QMatrix4x4 transformMatrix; int firstVertex = 0; diff --git a/src/utils/drm_format_helper.h b/src/utils/drm_format_helper.h index 7f004ac671..1dc2095489 100644 --- a/src/utils/drm_format_helper.h +++ b/src/utils/drm_format_helper.h @@ -7,11 +7,32 @@ SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once +#include +#include +#include + #include #include #include #include +struct YuvFormat +{ + uint32_t format = DRM_FORMAT_YUYV; + uint32_t widthDivisor = 1; + uint32_t heightDivisor = 1; +}; +struct YuvConversion +{ + QList plane = {}; +}; + +static const QHash s_drmConversions = { + {DRM_FORMAT_NV12, YuvConversion{ + {YuvFormat{DRM_FORMAT_R8, 1, 1}, YuvFormat{DRM_FORMAT_GR88, 2, 2}}, + }}, +}; + struct FormatInfo { uint32_t drmFormat; @@ -19,6 +40,12 @@ struct FormatInfo uint32_t alphaBits; uint32_t bitsPerPixel; GLint openglFormat; + + std::optional yuvConversion() const + { + const auto it = s_drmConversions.find(drmFormat); + return it != s_drmConversions.end() ? *it : std::optional{}; + } }; static std::optional formatInfo(uint32_t format) @@ -108,7 +135,27 @@ static std::optional formatInfo(uint32_t format) .bitsPerPixel = 16, .openglFormat = GL_RGB5_A1, }; + case DRM_FORMAT_NV12: + return FormatInfo{ + .drmFormat = format, + .bitsPerColor = 8, + .alphaBits = 0, + .bitsPerPixel = 24, + .openglFormat = GL_R8, + }; default: return std::nullopt; } } + +static QString drmFormatName(const QString &prefix, uint32_t format) +{ + return QString::asprintf( + "%s%c%c%c%c %s-endian (0x%08x)", prefix.toUtf8().constData(), + QLatin1Char(format & 0xff).toLatin1(), + QLatin1Char((format >> 8) & 0xff).toLatin1(), + QLatin1Char((format >> 16) & 0xff).toLatin1(), + QLatin1Char((format >> 24) & 0x7f).toLatin1(), + format & DRM_FORMAT_BIG_ENDIAN ? "big" : "little", + format); +}