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:
Aleix Pol Gonzalez 2023-10-26 15:37:35 +02:00 committed by Aleix Pol
parent 9e81d2f65c
commit 3568829216
15 changed files with 313 additions and 50 deletions

View file

@ -433,9 +433,9 @@ EglSurfaceTextureX11::EglSurfaceTextureX11(EglBackend *backend, SurfacePixmapX11
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)) {
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 &region)
{
// mipmaps need to be updated
m_texture->setDirty();
m_texture.setDirty();
}
EglPixmapTexture::EglPixmapTexture(EglBackend *backend)

View file

@ -788,9 +788,9 @@ GlxSurfaceTextureX11::GlxSurfaceTextureX11(GlxBackend *backend, SurfacePixmapX11
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)) {
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 &region)
{
// mipmaps need to be updated
m_texture->setDirty();
m_texture.setDirty();
}
GlxPixmapTexture::GlxPixmapTexture(GlxBackend *backend)

View file

@ -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");

View file

@ -109,6 +109,8 @@ public:
TextureHeight,
SourceNamedTransferFunction,
DestinationNamedTransferFunction,
Sampler,
Sampler1,
IntUniformCount
};

View file

@ -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 << " 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";

View file

@ -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<GLTexture> AbstractEglBackend::importDmaBufAsTexture(const DmaBufAttributes &attributes) const
{
return m_context->importDmaBufAsTexture(attributes);

View file

@ -46,7 +46,9 @@ public:
std::shared_ptr<GLTexture> 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<QByteArray> m_clientExtensions;
const dev_t m_deviceId;
QList<LinuxDmaBufV1Feedback::Tranche> m_tranches;
QHash<GraphicsBuffer *, EGLImageKHR> m_importedBuffers;
QHash<std::pair<GraphicsBuffer *, int>, EGLImageKHR> m_importedBuffers;
};
}

View file

@ -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 <epoxy/egl.h>
#include <utils/drm_format_helper.h>
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<GLTexture> 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);
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 false;
return std::shared_ptr<GLTexture>();
}
m_texture = std::make_unique<GLTexture>(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();
auto texture = std::make_shared<GLTexture>(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<GLeglImageOES>(image));
m_texture->unbind();
texture->unbind();
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 &currentPlane = 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<GLeglImageOES>(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 &currentPlane = 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

View file

@ -15,6 +15,7 @@
#include <QOpenGLContext>
#include <drm_fourcc.h>
#include <utils/drm_format_helper.h>
#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<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
{
return m_importFormats;

View file

@ -13,6 +13,7 @@
#include <QByteArray>
#include <QHash>
#include <QList>
#include <QSize>
#include <epoxy/egl.h>
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<EglDisplay> create(::EGLDisplay display, bool owning = true);

View file

@ -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

View file

@ -11,9 +11,38 @@
namespace KWin
{
class GLShader;
class GLTexture;
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
{
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 &region) = 0;
protected:
OpenGLBackend *m_backend;
std::unique_ptr<GLTexture> m_texture;
OpenGLSurfaceContents m_texture;
};
} // namespace KWin

View file

@ -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<GLTexture *>(renderNode.texture) && !std::get<GLTexture *>(renderNode.texture))
|| (std::holds_alternative<OpenGLSurfaceContents>(renderNode.texture) && !std::get<OpenGLSurfaceContents>(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<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));
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<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,
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) {
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<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);
vbo->draw(region, GL_TRIANGLES, renderNode.firstVertex,

View file

@ -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<GLTexture *, OpenGLSurfaceContents> texture;
RenderGeometry geometry;
QMatrix4x4 transformMatrix;
int firstVertex = 0;

View file

@ -7,11 +7,32 @@
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <QHash>
#include <QList>
#include <QString>
#include <epoxy/gl.h>
#include <libdrm/drm_fourcc.h>
#include <optional>
#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
{
uint32_t drmFormat;
@ -19,6 +40,12 @@ struct FormatInfo
uint32_t alphaBits;
uint32_t bitsPerPixel;
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)
@ -108,7 +135,27 @@ static std::optional<FormatInfo> 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);
}