opengl: Add support for NV12 on Wayland dmabufs
Offers wayland clients the possibility to send us their contents using an NV12 texture. It implements it by rendering into scene using a shader.
This commit is contained in:
parent
9e81d2f65c
commit
3568829216
15 changed files with 313 additions and 50 deletions
|
@ -433,9 +433,9 @@ EglSurfaceTextureX11::EglSurfaceTextureX11(EglBackend *backend, SurfacePixmapX11
|
||||||
|
|
||||||
bool EglSurfaceTextureX11::create()
|
bool EglSurfaceTextureX11::create()
|
||||||
{
|
{
|
||||||
auto texture = std::make_unique<EglPixmapTexture>(static_cast<EglBackend *>(m_backend));
|
auto texture = std::make_shared<EglPixmapTexture>(static_cast<EglBackend *>(m_backend));
|
||||||
if (texture->create(m_pixmap)) {
|
if (texture->create(m_pixmap)) {
|
||||||
m_texture = std::move(texture);
|
m_texture = {texture};
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -445,7 +445,7 @@ bool EglSurfaceTextureX11::create()
|
||||||
void EglSurfaceTextureX11::update(const QRegion ®ion)
|
void EglSurfaceTextureX11::update(const QRegion ®ion)
|
||||||
{
|
{
|
||||||
// mipmaps need to be updated
|
// mipmaps need to be updated
|
||||||
m_texture->setDirty();
|
m_texture.setDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
EglPixmapTexture::EglPixmapTexture(EglBackend *backend)
|
EglPixmapTexture::EglPixmapTexture(EglBackend *backend)
|
||||||
|
|
|
@ -788,9 +788,9 @@ GlxSurfaceTextureX11::GlxSurfaceTextureX11(GlxBackend *backend, SurfacePixmapX11
|
||||||
|
|
||||||
bool GlxSurfaceTextureX11::create()
|
bool GlxSurfaceTextureX11::create()
|
||||||
{
|
{
|
||||||
auto texture = std::make_unique<GlxPixmapTexture>(static_cast<GlxBackend *>(m_backend));
|
auto texture = std::make_shared<GlxPixmapTexture>(static_cast<GlxBackend *>(m_backend));
|
||||||
if (texture->create(m_pixmap)) {
|
if (texture->create(m_pixmap)) {
|
||||||
m_texture = std::move(texture);
|
m_texture = {texture};
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -800,7 +800,7 @@ bool GlxSurfaceTextureX11::create()
|
||||||
void GlxSurfaceTextureX11::update(const QRegion ®ion)
|
void GlxSurfaceTextureX11::update(const QRegion ®ion)
|
||||||
{
|
{
|
||||||
// mipmaps need to be updated
|
// mipmaps need to be updated
|
||||||
m_texture->setDirty();
|
m_texture.setDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
GlxPixmapTexture::GlxPixmapTexture(GlxBackend *backend)
|
GlxPixmapTexture::GlxPixmapTexture(GlxBackend *backend)
|
||||||
|
|
|
@ -225,6 +225,8 @@ void GLShader::resolveLocations()
|
||||||
|
|
||||||
m_intLocation[TextureWidth] = uniformLocation("textureWidth");
|
m_intLocation[TextureWidth] = uniformLocation("textureWidth");
|
||||||
m_intLocation[TextureHeight] = uniformLocation("textureHeight");
|
m_intLocation[TextureHeight] = uniformLocation("textureHeight");
|
||||||
|
m_intLocation[Sampler] = uniformLocation("sampler");
|
||||||
|
m_intLocation[Sampler1] = uniformLocation("sampler1");
|
||||||
m_intLocation[SourceNamedTransferFunction] = uniformLocation("sourceNamedTransferFunction");
|
m_intLocation[SourceNamedTransferFunction] = uniformLocation("sourceNamedTransferFunction");
|
||||||
m_intLocation[DestinationNamedTransferFunction] = uniformLocation("destinationNamedTransferFunction");
|
m_intLocation[DestinationNamedTransferFunction] = uniformLocation("destinationNamedTransferFunction");
|
||||||
|
|
||||||
|
|
|
@ -109,6 +109,8 @@ public:
|
||||||
TextureHeight,
|
TextureHeight,
|
||||||
SourceNamedTransferFunction,
|
SourceNamedTransferFunction,
|
||||||
DestinationNamedTransferFunction,
|
DestinationNamedTransferFunction,
|
||||||
|
Sampler,
|
||||||
|
Sampler1,
|
||||||
IntUniformCount
|
IntUniformCount
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -133,6 +133,8 @@ QByteArray ShaderManager::generateFragmentSource(ShaderTraits traits) const
|
||||||
|
|
||||||
if (traits & ShaderTrait::MapTexture) {
|
if (traits & ShaderTrait::MapTexture) {
|
||||||
stream << "uniform sampler2D sampler;\n";
|
stream << "uniform sampler2D sampler;\n";
|
||||||
|
stream << "uniform sampler2D sampler1;\n";
|
||||||
|
stream << "uniform int converter;\n";
|
||||||
stream << varying << " vec2 texcoord0;\n";
|
stream << varying << " vec2 texcoord0;\n";
|
||||||
} else if (traits & ShaderTrait::MapExternalTexture) {
|
} else if (traits & ShaderTrait::MapExternalTexture) {
|
||||||
stream << "#extension GL_OES_EGL_image_external : require\n\n";
|
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";
|
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 << "\nvoid main(void)\n{\n";
|
||||||
stream << " vec4 result;\n";
|
stream << " vec4 result;\n";
|
||||||
if (traits & ShaderTrait::MapTexture) {
|
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) {
|
} else if (traits & ShaderTrait::MapExternalTexture) {
|
||||||
// external textures require texture2D for sampling
|
// external textures require texture2D for sampling
|
||||||
stream << " result = texture2D(sampler, texcoord0);\n";
|
stream << " result = texture2D(sampler, texcoord0);\n";
|
||||||
|
|
|
@ -257,9 +257,32 @@ bool AbstractEglBackend::prefer10bpc() const
|
||||||
return false;
|
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)
|
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())) {
|
if (Q_LIKELY(it != m_importedBuffers.constEnd())) {
|
||||||
return *it;
|
return *it;
|
||||||
}
|
}
|
||||||
|
@ -267,10 +290,12 @@ EGLImageKHR AbstractEglBackend::importBufferAsImage(GraphicsBuffer *buffer)
|
||||||
Q_ASSERT(buffer->dmabufAttributes());
|
Q_ASSERT(buffer->dmabufAttributes());
|
||||||
EGLImageKHR image = importDmaBufAsImage(*buffer->dmabufAttributes());
|
EGLImageKHR image = importDmaBufAsImage(*buffer->dmabufAttributes());
|
||||||
if (image != EGL_NO_IMAGE_KHR) {
|
if (image != EGL_NO_IMAGE_KHR) {
|
||||||
m_importedBuffers[buffer] = image;
|
m_importedBuffers[key] = image;
|
||||||
connect(buffer, &QObject::destroyed, this, [this, buffer]() {
|
connect(buffer, &QObject::destroyed, this, [this, key]() {
|
||||||
eglDestroyImageKHR(m_display->handle(), m_importedBuffers.take(buffer));
|
eglDestroyImageKHR(m_display->handle(), m_importedBuffers.take(key));
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
qCWarning(KWIN_OPENGL) << "failed to import dmabuf" << buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
return image;
|
return image;
|
||||||
|
@ -281,6 +306,11 @@ EGLImageKHR AbstractEglBackend::importDmaBufAsImage(const DmaBufAttributes &dmab
|
||||||
return m_display->importDmaBufAsImage(dmabuf);
|
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<GLTexture> AbstractEglBackend::importDmaBufAsTexture(const DmaBufAttributes &attributes) const
|
std::shared_ptr<GLTexture> AbstractEglBackend::importDmaBufAsTexture(const DmaBufAttributes &attributes) const
|
||||||
{
|
{
|
||||||
return m_context->importDmaBufAsTexture(attributes);
|
return m_context->importDmaBufAsTexture(attributes);
|
||||||
|
|
|
@ -46,7 +46,9 @@ public:
|
||||||
|
|
||||||
std::shared_ptr<GLTexture> importDmaBufAsTexture(const DmaBufAttributes &attributes) const;
|
std::shared_ptr<GLTexture> importDmaBufAsTexture(const DmaBufAttributes &attributes) const;
|
||||||
EGLImageKHR importDmaBufAsImage(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);
|
||||||
|
EGLImageKHR importBufferAsImage(GraphicsBuffer *buffer, int plane, int format, const QSize &size);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
AbstractEglBackend(dev_t deviceId = 0);
|
AbstractEglBackend(dev_t deviceId = 0);
|
||||||
|
@ -72,7 +74,7 @@ protected:
|
||||||
QList<QByteArray> m_clientExtensions;
|
QList<QByteArray> m_clientExtensions;
|
||||||
const dev_t m_deviceId;
|
const dev_t m_deviceId;
|
||||||
QList<LinuxDmaBufV1Feedback::Tranche> m_tranches;
|
QList<LinuxDmaBufV1Feedback::Tranche> m_tranches;
|
||||||
QHash<GraphicsBuffer *, EGLImageKHR> m_importedBuffers;
|
QHash<std::pair<GraphicsBuffer *, int>, EGLImageKHR> m_importedBuffers;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,15 @@
|
||||||
|
|
||||||
#include "platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.h"
|
#include "platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.h"
|
||||||
#include "core/graphicsbufferview.h"
|
#include "core/graphicsbufferview.h"
|
||||||
|
#include "opengl/glshader.h"
|
||||||
|
#include "opengl/glshadermanager.h"
|
||||||
#include "opengl/gltexture.h"
|
#include "opengl/gltexture.h"
|
||||||
#include "platformsupport/scenes/opengl/abstract_egl_backend.h"
|
#include "platformsupport/scenes/opengl/abstract_egl_backend.h"
|
||||||
#include "utils/common.h"
|
#include "utils/common.h"
|
||||||
|
|
||||||
|
#include "abstract_egl_backend.h"
|
||||||
#include <epoxy/egl.h>
|
#include <epoxy/egl.h>
|
||||||
|
#include <utils/drm_format_helper.h>
|
||||||
|
|
||||||
namespace KWin
|
namespace KWin
|
||||||
{
|
{
|
||||||
|
@ -63,14 +67,17 @@ bool BasicEGLSurfaceTextureWayland::loadShmTexture(GraphicsBuffer *buffer)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_texture = GLTexture::upload(*view.image());
|
std::shared_ptr<GLTexture> texture = GLTexture::upload(*view.image());
|
||||||
if (Q_UNLIKELY(!m_texture)) {
|
if (Q_UNLIKELY(!texture)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_texture->setFilter(GL_LINEAR);
|
texture->setFilter(GL_LINEAR);
|
||||||
m_texture->setWrapMode(GL_CLAMP_TO_EDGE);
|
texture->setWrapMode(GL_CLAMP_TO_EDGE);
|
||||||
m_texture->setContentTransform(TextureTransform::MirrorY);
|
texture->setContentTransform(TextureTransform::MirrorY);
|
||||||
|
|
||||||
|
m_texture = {{texture}};
|
||||||
|
|
||||||
m_bufferType = BufferType::Shm;
|
m_bufferType = BufferType::Shm;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -90,28 +97,55 @@ void BasicEGLSurfaceTextureWayland::updateShmTexture(GraphicsBuffer *buffer, con
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const QRect &rect : region) {
|
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)
|
bool BasicEGLSurfaceTextureWayland::loadDmabufTexture(GraphicsBuffer *buffer)
|
||||||
{
|
{
|
||||||
EGLImageKHR image = backend()->importBufferAsImage(buffer);
|
auto createTexture = [this](EGLImageKHR image, const QSize &size) {
|
||||||
if (Q_UNLIKELY(image == EGL_NO_IMAGE_KHR)) {
|
if (Q_UNLIKELY(image == EGL_NO_IMAGE_KHR)) {
|
||||||
qCritical(KWIN_OPENGL) << "Invalid dmabuf-based wl_buffer";
|
qCritical(KWIN_OPENGL) << "Invalid dmabuf-based wl_buffer";
|
||||||
return false;
|
return std::shared_ptr<GLTexture>();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_texture = std::make_unique<GLTexture>(GL_TEXTURE_2D);
|
auto texture = std::make_shared<GLTexture>(GL_TEXTURE_2D);
|
||||||
m_texture->setSize(buffer->size());
|
texture->setSize(size);
|
||||||
m_texture->create();
|
texture->create();
|
||||||
m_texture->setWrapMode(GL_CLAMP_TO_EDGE);
|
texture->setWrapMode(GL_CLAMP_TO_EDGE);
|
||||||
m_texture->setFilter(GL_LINEAR);
|
texture->setFilter(GL_LINEAR);
|
||||||
m_texture->bind();
|
texture->bind();
|
||||||
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, static_cast<GLeglImageOES>(image));
|
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, static_cast<GLeglImageOES>(image));
|
||||||
m_texture->unbind();
|
texture->unbind();
|
||||||
if (m_pixmap->bufferOrigin() == GraphicsBufferOrigin::TopLeft) {
|
if (m_pixmap->bufferOrigin() == GraphicsBufferOrigin::TopLeft) {
|
||||||
m_texture->setContentTransform(TextureTransform::MirrorY);
|
texture->setContentTransform(TextureTransform::MirrorY);
|
||||||
|
}
|
||||||
|
return texture;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto format = formatInfo(buffer->dmabufAttributes()->format);
|
||||||
|
if (format && format->yuvConversion) {
|
||||||
|
QList<std::shared_ptr<GLTexture>> 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;
|
m_bufferType = BufferType::DmaBuf;
|
||||||
|
|
||||||
|
@ -126,9 +160,26 @@ void BasicEGLSurfaceTextureWayland::updateDmabufTexture(GraphicsBuffer *buffer)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_texture->bind();
|
const GLint target = GL_TEXTURE_2D;
|
||||||
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, static_cast<GLeglImageOES>(backend()->importBufferAsImage(buffer)));
|
auto format = formatInfo(buffer->dmabufAttributes()->format);
|
||||||
m_texture->unbind();
|
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<GLeglImageOES>(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<GLeglImageOES>(backend()->importBufferAsImage(buffer)));
|
||||||
|
m_texture.planes[0]->unbind();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace KWin
|
} // namespace KWin
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
#include <QOpenGLContext>
|
#include <QOpenGLContext>
|
||||||
#include <drm_fourcc.h>
|
#include <drm_fourcc.h>
|
||||||
|
#include <utils/drm_format_helper.h>
|
||||||
|
|
||||||
#ifndef EGL_DRM_RENDER_NODE_FILE_EXT
|
#ifndef EGL_DRM_RENDER_NODE_FILE_EXT
|
||||||
#define EGL_DRM_RENDER_NODE_FILE_EXT 0x3377
|
#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());
|
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<EGLint> 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<uint32_t, QList<uint64_t>> EglDisplay::supportedDrmFormats() const
|
QHash<uint32_t, QList<uint64_t>> EglDisplay::supportedDrmFormats() const
|
||||||
{
|
{
|
||||||
return m_importFormats;
|
return m_importFormats;
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
|
#include <QSize>
|
||||||
#include <epoxy/egl.h>
|
#include <epoxy/egl.h>
|
||||||
|
|
||||||
namespace KWin
|
namespace KWin
|
||||||
|
@ -43,6 +44,7 @@ public:
|
||||||
bool isExternalOnly(uint32_t format, uint64_t modifier) const;
|
bool isExternalOnly(uint32_t format, uint64_t modifier) const;
|
||||||
|
|
||||||
EGLImageKHR importDmaBufAsImage(const DmaBufAttributes &dmabuf) const;
|
EGLImageKHR importDmaBufAsImage(const DmaBufAttributes &dmabuf) const;
|
||||||
|
EGLImageKHR importDmaBufAsImage(const DmaBufAttributes &dmabuf, int plane, int format, const QSize &size) const;
|
||||||
|
|
||||||
static std::unique_ptr<EglDisplay> create(::EGLDisplay display, bool owning = true);
|
static std::unique_ptr<EglDisplay> create(::EGLDisplay display, bool owning = true);
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ OpenGLSurfaceTexture::~OpenGLSurfaceTexture()
|
||||||
|
|
||||||
bool OpenGLSurfaceTexture::isValid() const
|
bool OpenGLSurfaceTexture::isValid() const
|
||||||
{
|
{
|
||||||
return m_texture != nullptr;
|
return m_texture.isValid();
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenGLBackend *OpenGLSurfaceTexture::backend() const
|
OpenGLBackend *OpenGLSurfaceTexture::backend() const
|
||||||
|
@ -29,9 +29,16 @@ OpenGLBackend *OpenGLSurfaceTexture::backend() const
|
||||||
return m_backend;
|
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
|
} // namespace KWin
|
||||||
|
|
|
@ -11,9 +11,38 @@
|
||||||
namespace KWin
|
namespace KWin
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class GLShader;
|
||||||
class GLTexture;
|
class GLTexture;
|
||||||
class OpenGLBackend;
|
class OpenGLBackend;
|
||||||
|
|
||||||
|
class KWIN_EXPORT OpenGLSurfaceContents
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OpenGLSurfaceContents()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
OpenGLSurfaceContents(const std::shared_ptr<GLTexture> &contents)
|
||||||
|
: planes({contents})
|
||||||
|
{
|
||||||
|
}
|
||||||
|
OpenGLSurfaceContents(const QList<std::shared_ptr<GLTexture>> &planes)
|
||||||
|
: planes(planes)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDirty();
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
planes.clear();
|
||||||
|
}
|
||||||
|
bool isValid() const
|
||||||
|
{
|
||||||
|
return !planes.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<std::shared_ptr<GLTexture>> planes;
|
||||||
|
};
|
||||||
|
|
||||||
class KWIN_EXPORT OpenGLSurfaceTexture : public SurfaceTexture
|
class KWIN_EXPORT OpenGLSurfaceTexture : public SurfaceTexture
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -23,14 +52,14 @@ public:
|
||||||
bool isValid() const override;
|
bool isValid() const override;
|
||||||
|
|
||||||
OpenGLBackend *backend() const;
|
OpenGLBackend *backend() const;
|
||||||
GLTexture *texture() const;
|
OpenGLSurfaceContents texture() const;
|
||||||
|
|
||||||
virtual bool create() = 0;
|
virtual bool create() = 0;
|
||||||
virtual void update(const QRegion ®ion) = 0;
|
virtual void update(const QRegion ®ion) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
OpenGLBackend *m_backend;
|
OpenGLBackend *m_backend;
|
||||||
std::unique_ptr<GLTexture> m_texture;
|
OpenGLSurfaceContents m_texture;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace KWin
|
} // namespace KWin
|
||||||
|
|
|
@ -64,7 +64,7 @@ void ItemRendererOpenGL::setBlendEnabled(bool enabled)
|
||||||
m_blendingEnabled = enabled;
|
m_blendingEnabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GLTexture *bindSurfaceTexture(SurfaceItem *surfaceItem)
|
static OpenGLSurfaceContents bindSurfaceTexture(SurfaceItem *surfaceItem)
|
||||||
{
|
{
|
||||||
SurfacePixmap *surfacePixmap = surfaceItem->pixmap();
|
SurfacePixmap *surfacePixmap = surfaceItem->pixmap();
|
||||||
auto platformSurfaceTexture =
|
auto platformSurfaceTexture =
|
||||||
|
@ -73,7 +73,7 @@ static GLTexture *bindSurfaceTexture(SurfaceItem *surfaceItem)
|
||||||
return platformSurfaceTexture->texture();
|
return platformSurfaceTexture->texture();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (platformSurfaceTexture->texture()) {
|
if (platformSurfaceTexture->texture().isValid()) {
|
||||||
const QRegion region = surfaceItem->damage();
|
const QRegion region = surfaceItem->damage();
|
||||||
if (!region.isEmpty()) {
|
if (!region.isEmpty()) {
|
||||||
platformSurfaceTexture->update(region);
|
platformSurfaceTexture->update(region);
|
||||||
|
@ -81,11 +81,11 @@ static GLTexture *bindSurfaceTexture(SurfaceItem *surfaceItem)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!surfacePixmap->isValid()) {
|
if (!surfacePixmap->isValid()) {
|
||||||
return nullptr;
|
return {};
|
||||||
}
|
}
|
||||||
if (!platformSurfaceTexture->create()) {
|
if (!platformSurfaceTexture->create()) {
|
||||||
qCDebug(KWIN_OPENGL) << "Failed to bind window";
|
qCDebug(KWIN_OPENGL) << "Failed to bind window";
|
||||||
return nullptr;
|
return {};
|
||||||
}
|
}
|
||||||
surfaceItem->resetDamage();
|
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++) {
|
for (int i = 0, v = 0; i < renderContext.renderNodes.count(); i++) {
|
||||||
RenderNode &renderNode = renderContext.renderNodes[i];
|
RenderNode &renderNode = renderContext.renderNodes[i];
|
||||||
if (renderNode.geometry.isEmpty() || !renderNode.texture) {
|
if (renderNode.geometry.isEmpty()
|
||||||
|
|| (std::holds_alternative<GLTexture *>(renderNode.texture) && !std::get<GLTexture *>(renderNode.texture))
|
||||||
|
|| (std::holds_alternative<OpenGLSurfaceContents>(renderNode.texture) && !std::get<OpenGLSurfaceContents>(renderNode.texture).isValid())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderNode.firstVertex = v;
|
renderNode.firstVertex = v;
|
||||||
renderNode.vertexCount = renderNode.geometry.count();
|
renderNode.vertexCount = renderNode.geometry.count();
|
||||||
|
|
||||||
renderNode.geometry.postProcessTextureCoordinates(renderNode.texture->matrix(renderNode.coordinateType));
|
GLTexture *texture = nullptr;
|
||||||
|
if (std::holds_alternative<GLTexture *>(renderNode.texture)) {
|
||||||
|
texture = std::get<GLTexture *>(renderNode.texture);
|
||||||
|
} else {
|
||||||
|
texture = std::get<OpenGLSurfaceContents>(renderNode.texture).planes.constFirst().get();
|
||||||
|
}
|
||||||
|
renderNode.geometry.postProcessTextureCoordinates(texture->matrix(renderNode.coordinateType));
|
||||||
|
|
||||||
renderNode.geometry.copy(map->subspan(v));
|
renderNode.geometry.copy(map->subspan(v));
|
||||||
v += renderNode.geometry.count();
|
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::Saturation, data.saturation());
|
||||||
shader->setUniform(GLShader::Vec3Uniform::PrimaryBrightness, QVector3D(toXYZ(1, 0), toXYZ(1, 1), toXYZ(1, 2)));
|
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);
|
shader->setUniform(GLShader::ModelViewProjectionMatrix, renderContext.projectionMatrix * renderNode.transformMatrix);
|
||||||
if (traits & ShaderTrait::Modulate) {
|
if (traits & ShaderTrait::Modulate) {
|
||||||
|
@ -370,10 +383,34 @@ void ItemRendererOpenGL::renderItem(const RenderTarget &renderTarget, const Rend
|
||||||
shader->setColorspaceUniforms(renderNode.colorDescription, renderTarget.colorDescription());
|
shader->setColorspaceUniforms(renderNode.colorDescription, renderTarget.colorDescription());
|
||||||
}
|
}
|
||||||
|
|
||||||
renderNode.texture->bind();
|
if (std::holds_alternative<GLTexture *>(renderNode.texture)) {
|
||||||
|
const auto texture = std::get<GLTexture *>(renderNode.texture);
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
shader->setUniform("converter", 0);
|
||||||
|
texture->bind();
|
||||||
|
} else {
|
||||||
|
const auto contents = std::get<OpenGLSurfaceContents>(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,
|
vbo->draw(scissorRegion, GL_TRIANGLES, renderNode.firstVertex,
|
||||||
renderNode.vertexCount, renderContext.hardwareClipping);
|
renderNode.vertexCount, renderContext.hardwareClipping);
|
||||||
|
|
||||||
|
if (std::holds_alternative<GLTexture *>(renderNode.texture)) {
|
||||||
|
auto texture = std::get<GLTexture *>(renderNode.texture);
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
texture->unbind();
|
||||||
|
} else {
|
||||||
|
const auto contents = std::get<OpenGLSurfaceContents>(renderNode.texture);
|
||||||
|
for (int plane = 0; plane < contents.planes.count(); ++plane) {
|
||||||
|
glActiveTexture(GL_TEXTURE0 + plane);
|
||||||
|
contents.planes[plane]->unbind();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (shader) {
|
if (shader) {
|
||||||
ShaderManager::instance()->popShader();
|
ShaderManager::instance()->popShader();
|
||||||
|
@ -421,7 +458,16 @@ void ItemRendererOpenGL::visualizeFractional(const RenderViewport &viewport, con
|
||||||
|
|
||||||
setBlendEnabled(true);
|
setBlendEnabled(true);
|
||||||
|
|
||||||
m_debug.fractionalShader->setUniform("geometrySize", QVector2D(renderNode.texture->width(), renderNode.texture->height()));
|
QVector2D size;
|
||||||
|
if (std::holds_alternative<GLTexture *>(renderNode.texture)) {
|
||||||
|
auto texture = std::get<GLTexture *>(renderNode.texture);
|
||||||
|
size = QVector2D(texture->width(), texture->height());
|
||||||
|
} else {
|
||||||
|
auto texture = std::get<OpenGLSurfaceContents>(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);
|
m_debug.fractionalShader->setUniform(GLShader::ModelViewProjectionMatrix, renderContext.projectionMatrix * renderNode.transformMatrix);
|
||||||
|
|
||||||
vbo->draw(region, GL_TRIANGLES, renderNode.firstVertex,
|
vbo->draw(region, GL_TRIANGLES, renderNode.firstVertex,
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include "libkwineffects/kwineffects.h"
|
#include "libkwineffects/kwineffects.h"
|
||||||
#include "opengl/glutils.h"
|
#include "opengl/glutils.h"
|
||||||
|
#include "platformsupport/scenes/opengl/openglsurfacetexture.h"
|
||||||
#include "scene/itemrenderer.h"
|
#include "scene/itemrenderer.h"
|
||||||
|
|
||||||
namespace KWin
|
namespace KWin
|
||||||
|
@ -18,7 +19,7 @@ class KWIN_EXPORT ItemRendererOpenGL : public ItemRenderer
|
||||||
public:
|
public:
|
||||||
struct RenderNode
|
struct RenderNode
|
||||||
{
|
{
|
||||||
GLTexture *texture = nullptr;
|
std::variant<GLTexture *, OpenGLSurfaceContents> texture;
|
||||||
RenderGeometry geometry;
|
RenderGeometry geometry;
|
||||||
QMatrix4x4 transformMatrix;
|
QMatrix4x4 transformMatrix;
|
||||||
int firstVertex = 0;
|
int firstVertex = 0;
|
||||||
|
|
|
@ -7,11 +7,32 @@
|
||||||
SPDX-License-Identifier: GPL-2.0-or-later
|
SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <QHash>
|
||||||
|
#include <QList>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
#include <epoxy/gl.h>
|
#include <epoxy/gl.h>
|
||||||
#include <libdrm/drm_fourcc.h>
|
#include <libdrm/drm_fourcc.h>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct YuvFormat
|
||||||
|
{
|
||||||
|
uint32_t format = DRM_FORMAT_YUYV;
|
||||||
|
uint32_t widthDivisor = 1;
|
||||||
|
uint32_t heightDivisor = 1;
|
||||||
|
};
|
||||||
|
struct YuvConversion
|
||||||
|
{
|
||||||
|
QList<struct YuvFormat> plane = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
static const QHash<uint32_t, YuvConversion> s_drmConversions = {
|
||||||
|
{DRM_FORMAT_NV12, YuvConversion{
|
||||||
|
{YuvFormat{DRM_FORMAT_R8, 1, 1}, YuvFormat{DRM_FORMAT_GR88, 2, 2}},
|
||||||
|
}},
|
||||||
|
};
|
||||||
|
|
||||||
struct FormatInfo
|
struct FormatInfo
|
||||||
{
|
{
|
||||||
uint32_t drmFormat;
|
uint32_t drmFormat;
|
||||||
|
@ -19,6 +40,12 @@ struct FormatInfo
|
||||||
uint32_t alphaBits;
|
uint32_t alphaBits;
|
||||||
uint32_t bitsPerPixel;
|
uint32_t bitsPerPixel;
|
||||||
GLint openglFormat;
|
GLint openglFormat;
|
||||||
|
|
||||||
|
std::optional<YuvConversion> yuvConversion() const
|
||||||
|
{
|
||||||
|
const auto it = s_drmConversions.find(drmFormat);
|
||||||
|
return it != s_drmConversions.end() ? *it : std::optional<YuvConversion>{};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::optional<FormatInfo> formatInfo(uint32_t format)
|
static std::optional<FormatInfo> formatInfo(uint32_t format)
|
||||||
|
@ -108,7 +135,27 @@ static std::optional<FormatInfo> formatInfo(uint32_t format)
|
||||||
.bitsPerPixel = 16,
|
.bitsPerPixel = 16,
|
||||||
.openglFormat = GL_RGB5_A1,
|
.openglFormat = GL_RGB5_A1,
|
||||||
};
|
};
|
||||||
|
case DRM_FORMAT_NV12:
|
||||||
|
return FormatInfo{
|
||||||
|
.drmFormat = format,
|
||||||
|
.bitsPerColor = 8,
|
||||||
|
.alphaBits = 0,
|
||||||
|
.bitsPerPixel = 24,
|
||||||
|
.openglFormat = GL_R8,
|
||||||
|
};
|
||||||
default:
|
default:
|
||||||
return std::nullopt;
|
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);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue