/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2019 NVIDIA Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*********************************************************************/
#include "egl_stream_backend.h"
#include "composite.h"
#include "drm_backend.h"
#include "drm_output.h"
#include "drm_object_crtc.h"
#include "drm_object_plane.h"
#include "logging.h"
#include "logind.h"
#include "options.h"
#include "scene.h"
#include "screens.h"
#include "wayland_server.h"
#include
#include
#include
#include
#include
#include
#include
#include
namespace KWin
{
typedef EGLStreamKHR (*PFNEGLCREATESTREAMATTRIBNV)(EGLDisplay, EGLAttrib *);
typedef EGLBoolean (*PFNEGLGETOUTPUTLAYERSEXT)(EGLDisplay, EGLAttrib *, EGLOutputLayerEXT *, EGLint, EGLint *);
typedef EGLBoolean (*PFNEGLSTREAMCONSUMEROUTPUTEXT)(EGLDisplay, EGLStreamKHR, EGLOutputLayerEXT);
typedef EGLSurface (*PFNEGLCREATESTREAMPRODUCERSURFACEKHR)(EGLDisplay, EGLConfig, EGLStreamKHR, EGLint *);
typedef EGLBoolean (*PFNEGLDESTROYSTREAMKHR)(EGLDisplay, EGLStreamKHR);
typedef EGLBoolean (*PFNEGLSTREAMCONSUMERACQUIREATTRIBNV)(EGLDisplay, EGLStreamKHR, EGLAttrib *);
typedef EGLBoolean (*PFNEGLSTREAMCONSUMERGLTEXTUREEXTERNALKHR)(EGLDisplay, EGLStreamKHR);
typedef EGLBoolean (*PFNEGLQUERYSTREAMATTRIBNV)(EGLDisplay, EGLStreamKHR, EGLenum, EGLAttrib *);
typedef EGLBoolean (*PFNEGLSTREAMCONSUMERRELEASEKHR)(EGLDisplay, EGLStreamKHR);
typedef EGLBoolean (*PFNEGLQUERYWAYLANDBUFFERWL)(EGLDisplay, wl_resource *, EGLint, EGLint *);
PFNEGLCREATESTREAMATTRIBNV pEglCreateStreamAttribNV = nullptr;
PFNEGLGETOUTPUTLAYERSEXT pEglGetOutputLayersEXT = nullptr;
PFNEGLSTREAMCONSUMEROUTPUTEXT pEglStreamConsumerOutputEXT = nullptr;
PFNEGLCREATESTREAMPRODUCERSURFACEKHR pEglCreateStreamProducerSurfaceKHR = nullptr;
PFNEGLDESTROYSTREAMKHR pEglDestroyStreamKHR = nullptr;
PFNEGLSTREAMCONSUMERACQUIREATTRIBNV pEglStreamConsumerAcquireAttribNV = nullptr;
PFNEGLSTREAMCONSUMERGLTEXTUREEXTERNALKHR pEglStreamConsumerGLTextureExternalKHR = nullptr;
PFNEGLQUERYSTREAMATTRIBNV pEglQueryStreamAttribNV = nullptr;
PFNEGLSTREAMCONSUMERRELEASEKHR pEglStreamConsumerReleaseKHR = nullptr;
PFNEGLQUERYWAYLANDBUFFERWL pEglQueryWaylandBufferWL = nullptr;
#ifndef EGL_CONSUMER_AUTO_ACQUIRE_EXT
#define EGL_CONSUMER_AUTO_ACQUIRE_EXT 0x332B
#endif
#ifndef EGL_DRM_MASTER_FD_EXT
#define EGL_DRM_MASTER_FD_EXT 0x333C
#endif
#ifndef EGL_DRM_FLIP_EVENT_DATA_NV
#define EGL_DRM_FLIP_EVENT_DATA_NV 0x333E
#endif
#ifndef EGL_WAYLAND_EGLSTREAM_WL
#define EGL_WAYLAND_EGLSTREAM_WL 0x334B
#endif
#ifndef EGL_WAYLAND_Y_INVERTED_WL
#define EGL_WAYLAND_Y_INVERTED_WL 0x31DB
#endif
EglStreamBackend::EglStreamBackend(DrmBackend *b)
: AbstractEglBackend(), m_backend(b)
{
setIsDirectRendering(true);
setSyncsToVBlank(true);
connect(m_backend, &DrmBackend::outputAdded, this, &EglStreamBackend::createOutput);
connect(m_backend, &DrmBackend::outputRemoved, this,
[this] (DrmOutput *output) {
auto it = std::find_if(m_outputs.begin(), m_outputs.end(),
[output] (const Output &o) {
return o.output == output;
});
if (it == m_outputs.end()) {
return;
}
cleanupOutput(*it);
m_outputs.erase(it);
});
}
EglStreamBackend::~EglStreamBackend()
{
cleanup();
}
void EglStreamBackend::cleanupSurfaces()
{
for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) {
cleanupOutput(*it);
}
m_outputs.clear();
}
void EglStreamBackend::cleanupOutput(const Output &o)
{
if (o.buffer != nullptr) {
delete o.buffer;
}
if (o.eglSurface != EGL_NO_SURFACE) {
eglDestroySurface(eglDisplay(), o.eglSurface);
}
if (o.eglStream != EGL_NO_STREAM_KHR) {
pEglDestroyStreamKHR(eglDisplay(), o.eglStream);
}
}
bool EglStreamBackend::initializeEgl()
{
initClientExtensions();
EGLDisplay display = m_backend->sceneEglDisplay();
if (display == EGL_NO_DISPLAY) {
if (!hasClientExtension(QByteArrayLiteral("EGL_EXT_device_base")) &&
!(hasClientExtension(QByteArrayLiteral("EGL_EXT_device_query")) &&
hasClientExtension(QByteArrayLiteral("EGL_EXT_device_enumeration")))) {
setFailed("Missing required EGL client extension: "
"EGL_EXT_device_base or "
"EGL_EXT_device_query and EGL_EXT_device_enumeration");
return false;
}
// Try to find the EGLDevice corresponding to our DRM device file
int numDevices;
eglQueryDevicesEXT(0, nullptr, &numDevices);
QVector devices(numDevices);
eglQueryDevicesEXT(numDevices, devices.data(), &numDevices);
for (EGLDeviceEXT device : devices) {
const char *drmDeviceFile = eglQueryDeviceStringEXT(device, EGL_DRM_DEVICE_FILE_EXT);
if (m_backend->devNode() != drmDeviceFile) {
continue;
}
const char *deviceExtensionCString = eglQueryDeviceStringEXT(device, EGL_EXTENSIONS);
QByteArray deviceExtensions = QByteArray::fromRawData(deviceExtensionCString,
qstrlen(deviceExtensionCString));
if (!deviceExtensions.split(' ').contains(QByteArrayLiteral("EGL_EXT_device_drm"))) {
continue;
}
EGLint platformAttribs[] = {
EGL_DRM_MASTER_FD_EXT, m_backend->fd(),
EGL_NONE
};
display = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, device, platformAttribs);
break;
}
}
if (display == EGL_NO_DISPLAY) {
setFailed("No suitable EGL device found");
return false;
}
setEglDisplay(display);
if (!initEglAPI()) {
return false;
}
const QVector requiredExtensions = {
QByteArrayLiteral("EGL_EXT_output_base"),
QByteArrayLiteral("EGL_EXT_output_drm"),
QByteArrayLiteral("EGL_KHR_stream"),
QByteArrayLiteral("EGL_KHR_stream_producer_eglsurface"),
QByteArrayLiteral("EGL_EXT_stream_consumer_egloutput"),
QByteArrayLiteral("EGL_NV_stream_attrib"),
QByteArrayLiteral("EGL_EXT_stream_acquire_mode"),
QByteArrayLiteral("EGL_KHR_stream_consumer_gltexture"),
QByteArrayLiteral("EGL_WL_wayland_eglstream")
};
for (const QByteArray &ext : requiredExtensions) {
if (!hasExtension(ext)) {
setFailed(QStringLiteral("Missing required EGL extension: ") + ext);
return false;
}
}
pEglCreateStreamAttribNV = (PFNEGLCREATESTREAMATTRIBNV)eglGetProcAddress("eglCreateStreamAttribNV");
pEglGetOutputLayersEXT = (PFNEGLGETOUTPUTLAYERSEXT)eglGetProcAddress("eglGetOutputLayersEXT");
pEglStreamConsumerOutputEXT = (PFNEGLSTREAMCONSUMEROUTPUTEXT)eglGetProcAddress("eglStreamConsumerOutputEXT");
pEglCreateStreamProducerSurfaceKHR = (PFNEGLCREATESTREAMPRODUCERSURFACEKHR)eglGetProcAddress("eglCreateStreamProducerSurfaceKHR");
pEglDestroyStreamKHR = (PFNEGLDESTROYSTREAMKHR)eglGetProcAddress("eglDestroyStreamKHR");
pEglStreamConsumerAcquireAttribNV = (PFNEGLSTREAMCONSUMERACQUIREATTRIBNV)eglGetProcAddress("eglStreamConsumerAcquireAttribNV");
pEglStreamConsumerGLTextureExternalKHR = (PFNEGLSTREAMCONSUMERGLTEXTUREEXTERNALKHR)eglGetProcAddress("eglStreamConsumerGLTextureExternalKHR");
pEglQueryStreamAttribNV = (PFNEGLQUERYSTREAMATTRIBNV)eglGetProcAddress("eglQueryStreamAttribNV");
pEglStreamConsumerReleaseKHR = (PFNEGLSTREAMCONSUMERRELEASEKHR)eglGetProcAddress("eglStreamConsumerReleaseKHR");
pEglQueryWaylandBufferWL = (PFNEGLQUERYWAYLANDBUFFERWL)eglGetProcAddress("eglQueryWaylandBufferWL");
return true;
}
EglStreamBackend::StreamTexture *EglStreamBackend::lookupStreamTexture(KWayland::Server::SurfaceInterface *surface)
{
auto it = m_streamTextures.find(surface);
return it != m_streamTextures.end() ?
&it.value() :
nullptr;
}
void EglStreamBackend::attachStreamConsumer(KWayland::Server::SurfaceInterface *surface,
void *eglStream,
wl_array *attribs)
{
QVector streamAttribs;
streamAttribs << EGL_WAYLAND_EGLSTREAM_WL << (EGLAttrib)eglStream;
EGLAttrib *attribArray = (EGLAttrib *)attribs->data;
for (unsigned int i = 0; i < attribs->size; ++i) {
streamAttribs << attribArray[i];
}
streamAttribs << EGL_NONE;
EGLStreamKHR stream = pEglCreateStreamAttribNV(eglDisplay(), streamAttribs.data());
if (stream == EGL_NO_STREAM_KHR) {
qCWarning(KWIN_DRM) << "Failed to create EGL stream";
return;
}
GLuint texture;
StreamTexture *st = lookupStreamTexture(surface);
if (st != nullptr) {
pEglDestroyStreamKHR(eglDisplay(), st->stream);
st->stream = stream;
texture = st->texture;
} else {
StreamTexture newSt = { stream, 0 };
glGenTextures(1, &newSt.texture);
m_streamTextures.insert(surface, newSt);
texture = newSt.texture;
connect(surface, &KWayland::Server::Resource::unbound, this,
[surface, this]() {
const StreamTexture &st = m_streamTextures.take(surface);
pEglDestroyStreamKHR(eglDisplay(), st.stream);
glDeleteTextures(1, &st.texture);
});
}
glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture);
if (!pEglStreamConsumerGLTextureExternalKHR(eglDisplay(), stream)) {
qCWarning(KWIN_DRM) << "Failed to bind EGL stream to texture";
}
glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
}
void EglStreamBackend::init()
{
if (!m_backend->atomicModeSetting()) {
setFailed("EGLStream backend requires atomic modesetting");
return;
}
if (!initializeEgl()) {
setFailed("Failed to initialize EGL api");
return;
}
if (!initRenderingContext()) {
setFailed("Failed to initialize rendering context");
return;
}
initKWinGL();
setSupportsBufferAge(false);
initWayland();
using namespace KWayland::Server;
m_eglStreamControllerInterface = waylandServer()->display()->createEglStreamControllerInterface();
connect(m_eglStreamControllerInterface, &EglStreamControllerInterface::streamConsumerAttached, this,
&EglStreamBackend::attachStreamConsumer);
m_eglStreamControllerInterface->create();
if (!m_eglStreamControllerInterface->isValid()) {
setFailed("failed to initialize wayland-eglstream-controller interface");
}
}
bool EglStreamBackend::initRenderingContext()
{
initBufferConfigs();
if (!createContext()) {
return false;
}
const auto outputs = m_backend->drmOutputs();
for (DrmOutput *drmOutput : outputs) {
createOutput(drmOutput);
}
if (m_outputs.isEmpty()) {
qCCritical(KWIN_DRM) << "Failed to create output surface";
return false;
}
// set our first surface as the one for the abstract backend
setSurface(m_outputs.first().eglSurface);
return makeContextCurrent(m_outputs.first());
}
bool EglStreamBackend::resetOutput(Output &o, DrmOutput *drmOutput)
{
o.output = drmOutput;
if (o.buffer != nullptr) {
delete o.buffer;
}
// dumb buffer used for modesetting
o.buffer = m_backend->createBuffer(drmOutput->pixelSize());
EGLAttrib streamAttribs[] = {
EGL_STREAM_FIFO_LENGTH_KHR, 0, // mailbox mode
EGL_CONSUMER_AUTO_ACQUIRE_EXT, EGL_FALSE,
EGL_NONE
};
EGLStreamKHR stream = pEglCreateStreamAttribNV(eglDisplay(), streamAttribs);
if (stream == EGL_NO_STREAM_KHR) {
qCCritical(KWIN_DRM) << "Failed to create EGL stream for output";
return false;
}
EGLAttrib outputAttribs[3];
if (drmOutput->primaryPlane()) {
outputAttribs[0] = EGL_DRM_PLANE_EXT;
outputAttribs[1] = drmOutput->primaryPlane()->id();
} else {
outputAttribs[0] = EGL_DRM_CRTC_EXT;
outputAttribs[1] = drmOutput->crtc()->id();
}
outputAttribs[2] = EGL_NONE;
EGLint numLayers;
EGLOutputLayerEXT outputLayer;
pEglGetOutputLayersEXT(eglDisplay(), outputAttribs, &outputLayer, 1, &numLayers);
if (numLayers == 0) {
qCCritical(KWIN_DRM) << "No EGL output layers found";
return false;
}
pEglStreamConsumerOutputEXT(eglDisplay(), stream, outputLayer);
EGLint streamProducerAttribs[] = {
EGL_WIDTH, drmOutput->pixelSize().width(),
EGL_HEIGHT, drmOutput->pixelSize().height(),
EGL_NONE
};
EGLSurface eglSurface = pEglCreateStreamProducerSurfaceKHR(eglDisplay(), config(), stream,
streamProducerAttribs);
if (eglSurface == EGL_NO_SURFACE) {
qCCritical(KWIN_DRM) << "Failed to create EGL surface for output";
return false;
}
if (o.eglSurface != EGL_NO_SURFACE) {
if (surface() == o.eglSurface) {
setSurface(eglSurface);
}
eglDestroySurface(eglDisplay(), o.eglSurface);
}
if (o.eglStream != EGL_NO_STREAM_KHR) {
pEglDestroyStreamKHR(eglDisplay(), o.eglStream);
}
o.eglStream = stream;
o.eglSurface = eglSurface;
return true;
}
void EglStreamBackend::createOutput(DrmOutput *drmOutput)
{
Output o;
if (!resetOutput(o, drmOutput)) {
return;
}
connect(drmOutput, &DrmOutput::modeChanged, this,
[drmOutput, this] {
auto it = std::find_if(m_outputs.begin(), m_outputs.end(),
[drmOutput] (const auto &o) {
return o.output == drmOutput;
}
);
if (it == m_outputs.end()) {
return;
}
resetOutput(*it, drmOutput);
}
);
m_outputs << o;
}
bool EglStreamBackend::makeContextCurrent(const Output &output)
{
const EGLSurface surface = output.eglSurface;
if (surface == EGL_NO_SURFACE) {
return false;
}
if (eglMakeCurrent(eglDisplay(), surface, surface, context()) == EGL_FALSE) {
qCCritical(KWIN_DRM) << "Failed to make EGL context current";
return false;
}
EGLint error = eglGetError();
if (error != EGL_SUCCESS) {
qCWarning(KWIN_DRM) << "Error occurred while making EGL context current" << error;
return false;
}
const QSize &overall = screens()->size();
const QRect &v = output.output->geometry();
qreal scale = output.output->scale();
glViewport(-v.x() * scale, (v.height() - overall.height() + v.y()) * scale,
overall.width() * scale, overall.height() * scale);
return true;
}
bool EglStreamBackend::initBufferConfigs()
{
const EGLint configAttribs[] = {
EGL_SURFACE_TYPE, EGL_STREAM_BIT_KHR,
EGL_RED_SIZE, 1,
EGL_GREEN_SIZE, 1,
EGL_BLUE_SIZE, 1,
EGL_ALPHA_SIZE, 0,
EGL_RENDERABLE_TYPE, isOpenGLES() ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_BIT,
EGL_CONFIG_CAVEAT, EGL_NONE,
EGL_NONE,
};
EGLint count;
EGLConfig config;
if (!eglChooseConfig(eglDisplay(), configAttribs, &config, 1, &count)) {
qCCritical(KWIN_DRM) << "Failed to query available EGL configs";
return false;
}
if (count == 0) {
qCCritical(KWIN_DRM) << "No suitable EGL config found";
return false;
}
setConfig(config);
return true;
}
void EglStreamBackend::present()
{
for (auto &o : m_outputs) {
makeContextCurrent(o);
presentOnOutput(o);
}
}
void EglStreamBackend::presentOnOutput(EglStreamBackend::Output &o)
{
eglSwapBuffers(eglDisplay(), o.eglSurface);
if (!m_backend->present(o.buffer, o.output)) {
return;
}
EGLAttrib acquireAttribs[] = {
EGL_DRM_FLIP_EVENT_DATA_NV, (EGLAttrib)o.output,
EGL_NONE,
};
if (!pEglStreamConsumerAcquireAttribNV(eglDisplay(), o.eglStream, acquireAttribs)) {
qCWarning(KWIN_DRM) << "Failed to acquire output EGL stream frame";
}
}
void EglStreamBackend::screenGeometryChanged(const QSize &size)
{
Q_UNUSED(size)
}
SceneOpenGLTexturePrivate *EglStreamBackend::createBackendTexture(SceneOpenGLTexture *texture)
{
return new EglStreamTexture(texture, this);
}
QRegion EglStreamBackend::prepareRenderingFrame()
{
startRenderTimer();
return QRegion();
}
QRegion EglStreamBackend::prepareRenderingForScreen(int screenId)
{
const Output &o = m_outputs.at(screenId);
makeContextCurrent(o);
return o.output->geometry();
}
void EglStreamBackend::endRenderingFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
{
Q_UNUSED(renderedRegion)
Q_UNUSED(damagedRegion)
}
void EglStreamBackend::endRenderingFrameForScreen(int screenId, const QRegion &renderedRegion, const QRegion &damagedRegion)
{
Q_UNUSED(renderedRegion);
Q_UNUSED(damagedRegion);
Output &o = m_outputs[screenId];
presentOnOutput(o);
}
bool EglStreamBackend::usesOverlayWindow() const
{
return false;
}
bool EglStreamBackend::perScreenRendering() const
{
return true;
}
/************************************************
* EglTexture
************************************************/
EglStreamTexture::EglStreamTexture(SceneOpenGLTexture *texture, EglStreamBackend *backend)
: AbstractEglTexture(texture, backend), m_backend(backend), m_fbo(0), m_rbo(0)
{
}
EglStreamTexture::~EglStreamTexture()
{
glDeleteRenderbuffers(1, &m_rbo);
glDeleteFramebuffers(1, &m_fbo);
}
bool EglStreamTexture::acquireStreamFrame(EGLStreamKHR stream)
{
EGLAttrib streamState;
if (!pEglQueryStreamAttribNV(m_backend->eglDisplay(), stream,
EGL_STREAM_STATE_KHR, &streamState)) {
qCWarning(KWIN_DRM) << "Failed to query EGL stream state";
return false;
}
if (streamState == EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR) {
if (pEglStreamConsumerAcquireAttribNV(m_backend->eglDisplay(), stream, nullptr)) {
return true;
} else {
qCWarning(KWIN_DRM) << "Failed to acquire EGL stream frame";
}
}
// Re-use previous texture contents if no new frame is available
// or if acquisition fails for some reason
return false;
}
void EglStreamTexture::createFbo()
{
glDeleteRenderbuffers(1, &m_rbo);
glDeleteFramebuffers(1, &m_fbo);
glGenFramebuffers(1, &m_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
glGenRenderbuffers(1, &m_rbo);
glBindRenderbuffer(GL_RENDERBUFFER, m_rbo);
glRenderbufferStorage(GL_RENDERBUFFER, m_format, m_size.width(), m_size.height());
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_rbo);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
// Renders the contents of the given EXTERNAL_OES texture
// to the scratch framebuffer, then copies this to m_texture
void EglStreamTexture::copyExternalTexture(GLuint tex)
{
GLint oldViewport[4], oldProgram;
glGetIntegerv(GL_VIEWPORT, oldViewport);
glViewport(0, 0, m_size.width(), m_size.height());
glGetIntegerv(GL_CURRENT_PROGRAM, &oldProgram);
glUseProgram(0);
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
glBindRenderbuffer(GL_RENDERBUFFER, m_rbo);
glBindTexture(GL_TEXTURE_EXTERNAL_OES, tex);
glEnable(GL_TEXTURE_EXTERNAL_OES);
GLfloat yTop = texture()->isYInverted() ? 0 : 1;
glBegin(GL_QUADS);
glTexCoord2f(0, yTop);
glVertex2f(-1, 1);
glTexCoord2f(0, 1 - yTop);
glVertex2f(-1, -1);
glTexCoord2f(1, 1 - yTop);
glVertex2f(1, -1);
glTexCoord2f(1, yTop);
glVertex2f(1, 1);
glEnd();
texture()->bind();
glCopyTexImage2D(m_target, 0, m_format, 0, 0, m_size.width(), m_size.height(), 0);
texture()->unbind();
glDisable(GL_TEXTURE_EXTERNAL_OES);
glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glUseProgram(oldProgram);
glViewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]);
}
bool EglStreamTexture::attachBuffer(KWayland::Server::BufferInterface *buffer)
{
QSize oldSize = m_size;
m_size = buffer->size();
GLenum oldFormat = m_format;
m_format = buffer->hasAlphaChannel() ? GL_RGBA : GL_RGB;
EGLint yInverted, wasYInverted = texture()->isYInverted();
if (!pEglQueryWaylandBufferWL(m_backend->eglDisplay(), buffer->resource(), EGL_WAYLAND_Y_INVERTED_WL, &yInverted)) {
yInverted = EGL_TRUE;
}
texture()->setYInverted(yInverted);
updateMatrix();
return oldSize != m_size ||
oldFormat != m_format ||
wasYInverted != texture()->isYInverted();
}
bool EglStreamTexture::loadTexture(WindowPixmap *pixmap)
{
using namespace KWayland::Server;
SurfaceInterface *surface = pixmap->surface();
const EglStreamBackend::StreamTexture *st = m_backend->lookupStreamTexture(surface);
if (!pixmap->buffer().isNull() && st != nullptr) {
glGenTextures(1, &m_texture);
texture()->setWrapMode(GL_CLAMP_TO_EDGE);
texture()->setFilter(GL_LINEAR);
attachBuffer(surface->buffer());
createFbo();
surface->resetTrackedDamage();
if (acquireStreamFrame(st->stream)) {
copyExternalTexture(st->texture);
if (!pEglStreamConsumerReleaseKHR(m_backend->eglDisplay(), st->stream)) {
qCWarning(KWIN_DRM) << "Failed to release EGL stream";
}
}
return true;
} else {
// Not an EGLStream surface
return AbstractEglTexture::loadTexture(pixmap);
}
}
void EglStreamTexture::updateTexture(WindowPixmap *pixmap)
{
using namespace KWayland::Server;
SurfaceInterface *surface = pixmap->surface();
const EglStreamBackend::StreamTexture *st = m_backend->lookupStreamTexture(surface);
if (!pixmap->buffer().isNull() && st != nullptr) {
if (attachBuffer(surface->buffer())) {
createFbo();
}
surface->resetTrackedDamage();
if (acquireStreamFrame(st->stream)) {
copyExternalTexture(st->texture);
if (!pEglStreamConsumerReleaseKHR(m_backend->eglDisplay(), st->stream)) {
qCWarning(KWIN_DRM) << "Failed to release EGL stream";
}
}
} else {
// Not an EGLStream surface
AbstractEglTexture::updateTexture(pixmap);
}
}
} // namespace