File indexing completed on 2024-11-10 04:56:56

0001 /*
0002     SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.h"
0008 #include "core/graphicsbufferview.h"
0009 #include "opengl/glshader.h"
0010 #include "opengl/glshadermanager.h"
0011 #include "opengl/gltexture.h"
0012 #include "platformsupport/scenes/opengl/abstract_egl_backend.h"
0013 #include "utils/common.h"
0014 
0015 #include "abstract_egl_backend.h"
0016 #include <epoxy/egl.h>
0017 #include <utils/drm_format_helper.h>
0018 
0019 namespace KWin
0020 {
0021 
0022 BasicEGLSurfaceTextureWayland::BasicEGLSurfaceTextureWayland(OpenGLBackend *backend, SurfacePixmap *pixmap)
0023     : OpenGLSurfaceTextureWayland(backend, pixmap)
0024 {
0025 }
0026 
0027 BasicEGLSurfaceTextureWayland::~BasicEGLSurfaceTextureWayland()
0028 {
0029     destroy();
0030 }
0031 
0032 AbstractEglBackend *BasicEGLSurfaceTextureWayland::backend() const
0033 {
0034     return static_cast<AbstractEglBackend *>(m_backend);
0035 }
0036 
0037 bool BasicEGLSurfaceTextureWayland::create()
0038 {
0039     if (m_pixmap->buffer()->dmabufAttributes()) {
0040         return loadDmabufTexture(m_pixmap->buffer());
0041     } else if (m_pixmap->buffer()->shmAttributes()) {
0042         return loadShmTexture(m_pixmap->buffer());
0043     } else {
0044         return false;
0045     }
0046 }
0047 
0048 void BasicEGLSurfaceTextureWayland::destroy()
0049 {
0050     m_texture.reset();
0051     m_bufferType = BufferType::None;
0052 }
0053 
0054 void BasicEGLSurfaceTextureWayland::update(const QRegion &region)
0055 {
0056     if (m_pixmap->buffer()->dmabufAttributes()) {
0057         updateDmabufTexture(m_pixmap->buffer());
0058     } else if (m_pixmap->buffer()->shmAttributes()) {
0059         updateShmTexture(m_pixmap->buffer(), region);
0060     }
0061 }
0062 
0063 bool BasicEGLSurfaceTextureWayland::loadShmTexture(GraphicsBuffer *buffer)
0064 {
0065     const GraphicsBufferView view(buffer);
0066     if (Q_UNLIKELY(!view.image())) {
0067         return false;
0068     }
0069 
0070     std::shared_ptr<GLTexture> texture = GLTexture::upload(*view.image());
0071     if (Q_UNLIKELY(!texture)) {
0072         return false;
0073     }
0074 
0075     texture->setFilter(GL_LINEAR);
0076     texture->setWrapMode(GL_CLAMP_TO_EDGE);
0077     texture->setContentTransform(OutputTransform::FlipY);
0078 
0079     m_texture = {{texture}};
0080 
0081     m_bufferType = BufferType::Shm;
0082 
0083     return true;
0084 }
0085 
0086 void BasicEGLSurfaceTextureWayland::updateShmTexture(GraphicsBuffer *buffer, const QRegion &region)
0087 {
0088     if (Q_UNLIKELY(m_bufferType != BufferType::Shm)) {
0089         destroy();
0090         create();
0091         return;
0092     }
0093 
0094     const GraphicsBufferView view(buffer);
0095     if (Q_UNLIKELY(!view.image())) {
0096         return;
0097     }
0098 
0099     for (const QRect &rect : region) {
0100         m_texture.planes[0]->update(*view.image(), rect.topLeft(), rect);
0101     }
0102 }
0103 
0104 bool BasicEGLSurfaceTextureWayland::loadDmabufTexture(GraphicsBuffer *buffer)
0105 {
0106     auto createTexture = [this](EGLImageKHR image, const QSize &size, bool isExternalOnly) -> std::shared_ptr<GLTexture> {
0107         if (Q_UNLIKELY(image == EGL_NO_IMAGE_KHR)) {
0108             qCritical(KWIN_OPENGL) << "Invalid dmabuf-based wl_buffer";
0109             return nullptr;
0110         }
0111 
0112         GLint target = isExternalOnly ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D;
0113         auto texture = std::make_shared<GLTexture>(target);
0114         texture->setSize(size);
0115         if (!texture->create()) {
0116             return nullptr;
0117         }
0118         texture->setWrapMode(GL_CLAMP_TO_EDGE);
0119         texture->setFilter(GL_LINEAR);
0120         texture->bind();
0121         glEGLImageTargetTexture2DOES(target, static_cast<GLeglImageOES>(image));
0122         texture->unbind();
0123         if (m_pixmap->bufferOrigin() == GraphicsBufferOrigin::TopLeft) {
0124             texture->setContentTransform(OutputTransform::FlipY);
0125         }
0126         return texture;
0127     };
0128 
0129     const auto attribs = buffer->dmabufAttributes();
0130     if (auto itConv = s_drmConversions.find(buffer->dmabufAttributes()->format); itConv != s_drmConversions.end()) {
0131         QList<std::shared_ptr<GLTexture>> textures;
0132         Q_ASSERT(itConv->plane.count() == uint(buffer->dmabufAttributes()->planeCount));
0133 
0134         for (uint plane = 0; plane < itConv->plane.count(); ++plane) {
0135             const auto &currentPlane = itConv->plane[plane];
0136             QSize size = buffer->size();
0137             size.rwidth() /= currentPlane.widthDivisor;
0138             size.rheight() /= currentPlane.heightDivisor;
0139 
0140             const bool isExternal = backend()->eglDisplayObject()->isExternalOnly(currentPlane.format, attribs->modifier);
0141             auto t = createTexture(backend()->importBufferAsImage(buffer, plane, currentPlane.format, size), size, isExternal);
0142             if (!t) {
0143                 return false;
0144             }
0145             textures << t;
0146         }
0147         m_texture = {textures};
0148     } else {
0149         const bool isExternal = backend()->eglDisplayObject()->isExternalOnly(attribs->format, attribs->modifier);
0150         auto texture = createTexture(backend()->importBufferAsImage(buffer), buffer->size(), isExternal);
0151         if (!texture) {
0152             return false;
0153         }
0154         m_texture = {{texture}};
0155     }
0156     m_bufferType = BufferType::DmaBuf;
0157 
0158     return true;
0159 }
0160 
0161 void BasicEGLSurfaceTextureWayland::updateDmabufTexture(GraphicsBuffer *buffer)
0162 {
0163     if (Q_UNLIKELY(m_bufferType != BufferType::DmaBuf)) {
0164         destroy();
0165         create();
0166         return;
0167     }
0168 
0169     const GLint target = GL_TEXTURE_2D;
0170     if (auto itConv = s_drmConversions.find(buffer->dmabufAttributes()->format); itConv != s_drmConversions.end()) {
0171         Q_ASSERT(itConv->plane.count() == uint(buffer->dmabufAttributes()->planeCount));
0172         for (uint plane = 0; plane < itConv->plane.count(); ++plane) {
0173             const auto &currentPlane = itConv->plane[plane];
0174             QSize size = buffer->size();
0175             size.rwidth() /= currentPlane.widthDivisor;
0176             size.rheight() /= currentPlane.heightDivisor;
0177 
0178             m_texture.planes[plane]->bind();
0179             glEGLImageTargetTexture2DOES(target, static_cast<GLeglImageOES>(backend()->importBufferAsImage(buffer, plane, currentPlane.format, size)));
0180             m_texture.planes[plane]->unbind();
0181         }
0182     } else {
0183         Q_ASSERT(m_texture.planes.count() == 1);
0184         m_texture.planes[0]->bind();
0185         glEGLImageTargetTexture2DOES(target, static_cast<GLeglImageOES>(backend()->importBufferAsImage(buffer)));
0186         m_texture.planes[0]->unbind();
0187     }
0188 }
0189 
0190 } // namespace KWin