platforms/drm: kill the EglStreams backend
The proprietary NVidia driver now supports gbm, which vastly improves the user experience. For older devices that will not get gbm support dropping EglStreams will likely not have a big impact as it has several session breaking issues anyways. By removing the backend a lot of logic can be simplified, most notably multi-gpu.
This commit is contained in:
parent
f91ae3e975
commit
bad5752110
11 changed files with 6 additions and 1009 deletions
|
@ -230,11 +230,6 @@ else()
|
|||
set(HAVE_GBM_BO_GET_FD_FOR_PLANE 0)
|
||||
endif()
|
||||
|
||||
option(KWIN_BUILD_EGL_STREAM_BACKEND "Enable building of EGLStream based DRM backend" ON)
|
||||
if (KWIN_BUILD_EGL_STREAM_BACKEND)
|
||||
set(HAVE_EGL_STREAMS TRUE)
|
||||
endif()
|
||||
|
||||
find_package(X11)
|
||||
set_package_properties(X11 PROPERTIES
|
||||
DESCRIPTION "X11 libraries"
|
||||
|
|
|
@ -30,12 +30,6 @@ if (HAVE_GBM)
|
|||
)
|
||||
endif()
|
||||
|
||||
if (HAVE_EGL_STREAMS)
|
||||
set(DRM_SOURCES ${DRM_SOURCES}
|
||||
egl_stream_backend.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
add_library(KWinWaylandDrmBackend MODULE ${DRM_SOURCES})
|
||||
set_target_properties(KWinWaylandDrmBackend PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/org.kde.kwin.waylandbackends/")
|
||||
target_link_libraries(KWinWaylandDrmBackend kwin Libdrm::Libdrm)
|
||||
|
|
|
@ -31,9 +31,6 @@
|
|||
#include <gbm.h>
|
||||
#include "gbm_dmabuf.h"
|
||||
#endif
|
||||
#if HAVE_EGL_STREAMS
|
||||
#include "egl_stream_backend.h"
|
||||
#endif
|
||||
// KF5
|
||||
#include <KCoreAddons>
|
||||
#include <KLocalizedString>
|
||||
|
@ -190,17 +187,6 @@ bool DrmBackend::initialize()
|
|||
bootVga |= device->isBootVga();
|
||||
}
|
||||
}
|
||||
|
||||
// if a boot device is set, honor that setting
|
||||
// if not, prefer gbm for rendering because that works better
|
||||
if (!bootVga && !m_gpus.isEmpty() && m_gpus[0]->useEglStreams()) {
|
||||
for (int i = 1; i < m_gpus.count(); i++) {
|
||||
if (!m_gpus[i]->useEglStreams()) {
|
||||
m_gpus.swapItemsAt(i, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_gpus.isEmpty()) {
|
||||
|
@ -209,10 +195,6 @@ bool DrmBackend::initialize()
|
|||
}
|
||||
|
||||
initCursor();
|
||||
// workaround for BUG 438363: something goes wrong in scene initialization without a surface being current in EglStreamBackend
|
||||
if (m_gpus[0]->useEglStreams()) {
|
||||
updateOutputs();
|
||||
}
|
||||
|
||||
// setup udevMonitor
|
||||
if (m_udevMonitor) {
|
||||
|
@ -276,9 +258,6 @@ void DrmBackend::handleUdevEvent()
|
|||
|
||||
DrmGpu *DrmBackend::addGpu(const QString &fileName)
|
||||
{
|
||||
if (primaryGpu() && primaryGpu()->useEglStreams()) {
|
||||
return nullptr;
|
||||
}
|
||||
int fd = session()->openRestricted(fileName);
|
||||
if (fd < 0) {
|
||||
qCWarning(KWIN_DRM) << "failed to open drm device at" << fileName;
|
||||
|
@ -519,21 +498,6 @@ void DrmBackend::enableOutput(DrmAbstractOutput *output, bool enable)
|
|||
|
||||
void DrmBackend::initCursor()
|
||||
{
|
||||
|
||||
#if HAVE_EGL_STREAMS
|
||||
// Hardware cursors aren't currently supported with EGLStream backend,
|
||||
// possibly an NVIDIA driver bug
|
||||
bool needsSoftwareCursor = false;
|
||||
for (auto gpu : qAsConst(m_gpus)) {
|
||||
if (gpu->useEglStreams()) {
|
||||
needsSoftwareCursor = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
setSoftwareCursorForced(needsSoftwareCursor);
|
||||
#endif
|
||||
|
||||
// now we have screens and can set cursors, so start tracking
|
||||
connect(Cursors::self(), &Cursors::currentCursorChanged, this, &DrmBackend::updateCursor);
|
||||
connect(Cursors::self(), &Cursors::positionChanged, this, &DrmBackend::moveCursor);
|
||||
}
|
||||
|
@ -623,14 +587,6 @@ QPainterBackend *DrmBackend::createQPainterBackend()
|
|||
|
||||
OpenGLBackend *DrmBackend::createOpenGLBackend()
|
||||
{
|
||||
#if HAVE_EGL_STREAMS
|
||||
if (m_gpus.at(0)->useEglStreams()) {
|
||||
auto backend = new EglStreamBackend(this, m_gpus.at(0));
|
||||
AbstractEglBackend::setPrimaryBackend(backend);
|
||||
return backend;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAVE_GBM
|
||||
auto primaryBackend = new EglGbmBackend(this, m_gpus.at(0));
|
||||
AbstractEglBackend::setPrimaryBackend(primaryBackend);
|
||||
|
@ -656,10 +612,6 @@ QVector<CompositingType> DrmBackend::supportedCompositors() const
|
|||
}
|
||||
#if HAVE_GBM
|
||||
return QVector<CompositingType>{OpenGLCompositing, QPainterCompositing};
|
||||
#elif HAVE_EGL_STREAMS
|
||||
return m_gpus.at(0)->useEglStreams() ?
|
||||
QVector<CompositingType>{OpenGLCompositing, QPainterCompositing} :
|
||||
QVector<CompositingType>{QPainterCompositing};
|
||||
#else
|
||||
return QVector<CompositingType>{QPainterCompositing};
|
||||
#endif
|
||||
|
@ -675,9 +627,6 @@ QString DrmBackend::supportInformation() const
|
|||
for (int g = 0; g < m_gpus.size(); g++) {
|
||||
s << "Atomic Mode Setting on GPU " << g << ": " << m_gpus.at(g)->atomicModeSetting() << Qt::endl;
|
||||
}
|
||||
#if HAVE_EGL_STREAMS
|
||||
s << "Using EGL Streams: " << m_gpus.at(0)->useEglStreams() << Qt::endl;
|
||||
#endif
|
||||
return supportInfo;
|
||||
}
|
||||
|
||||
|
|
|
@ -80,16 +80,8 @@ DrmGpu::DrmGpu(DrmBackend *backend, const QString &devNode, int fd, dev_t device
|
|||
// find out if this GPU is using the NVidia proprietary driver
|
||||
DrmScopedPointer<drmVersion> version(drmGetVersion(fd));
|
||||
m_isNVidia = strstr(version->name, "nvidia-drm");
|
||||
m_useEglStreams = m_isNVidia;
|
||||
#if HAVE_GBM
|
||||
m_gbmDevice = gbm_create_device(m_fd);
|
||||
bool envVarIsSet = false;
|
||||
bool value = qEnvironmentVariableIntValue("KWIN_DRM_FORCE_EGL_STREAMS", &envVarIsSet) != 0;
|
||||
if (envVarIsSet) {
|
||||
m_useEglStreams = m_isNVidia && value;
|
||||
} else if (m_gbmDevice) {
|
||||
m_useEglStreams = m_isNVidia && strcmp(gbm_device_get_backend_name(m_gbmDevice), "nvidia") != 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_socketNotifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);
|
||||
|
@ -695,11 +687,6 @@ bool DrmGpu::atomicModeSetting() const
|
|||
return m_atomicModeSetting;
|
||||
}
|
||||
|
||||
bool DrmGpu::useEglStreams() const
|
||||
{
|
||||
return m_useEglStreams;
|
||||
}
|
||||
|
||||
QString DrmGpu::devNode() const
|
||||
{
|
||||
return m_devNode;
|
||||
|
|
|
@ -54,7 +54,6 @@ public:
|
|||
|
||||
bool atomicModeSetting() const;
|
||||
bool addFB2ModifiersSupported() const;
|
||||
bool useEglStreams() const;
|
||||
bool isNVidia() const;
|
||||
bool isFormatSupported(uint32_t drmFormat) const;
|
||||
gbm_device *gbmDevice() const;
|
||||
|
@ -112,7 +111,6 @@ private:
|
|||
const dev_t m_deviceId;
|
||||
const QString m_devNode;
|
||||
bool m_atomicModeSetting;
|
||||
bool m_useEglStreams;
|
||||
bool m_addFB2ModifiersSupported = false;
|
||||
bool m_isNVidia;
|
||||
clockid_t m_presentationClock;
|
||||
|
|
|
@ -62,13 +62,6 @@ bool DrmPipeline::present(const QSharedPointer<DrmBuffer> &buffer)
|
|||
return false;
|
||||
}
|
||||
m_primaryBuffer = buffer;
|
||||
if (gpu()->useEglStreams() && gpu()->eglBackend() != nullptr && gpu() == gpu()->platform()->primaryGpu()) {
|
||||
// EglStreamBackend queues normal page flips through EGL,
|
||||
// modesets etc are performed through DRM-KMS
|
||||
if (!m_connector->needsCommit() && !pending.crtc->needsCommit()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
bool directScanout = false;
|
||||
#if HAVE_GBM
|
||||
// with direct scanout disallow modesets, calling presentFailed() and logging warnings
|
||||
|
@ -254,8 +247,7 @@ bool DrmPipeline::populateAtomicValues(drmModeAtomicReq *req, uint32_t &flags)
|
|||
prepareModeset();
|
||||
flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
|
||||
}
|
||||
bool usesEglStreams = gpu()->useEglStreams() && gpu()->eglBackend() != nullptr && gpu() == gpu()->platform()->primaryGpu();
|
||||
if (!usesEglStreams && activePending()) {
|
||||
if (activePending()) {
|
||||
flags |= DRM_MODE_PAGE_FLIP_EVENT;
|
||||
}
|
||||
if (pending.crtc) {
|
||||
|
|
|
@ -13,9 +13,6 @@
|
|||
#if HAVE_GBM
|
||||
#include "egl_gbm_backend.h"
|
||||
#endif
|
||||
#if HAVE_EGL_STREAMS
|
||||
#include "egl_stream_backend.h"
|
||||
#endif
|
||||
#include "drm_backend.h"
|
||||
#include "drm_gpu.h"
|
||||
|
||||
|
@ -115,22 +112,13 @@ bool EglMultiBackend::directScanoutAllowed(AbstractOutput *output) const
|
|||
|
||||
void EglMultiBackend::addGpu(DrmGpu *gpu)
|
||||
{
|
||||
AbstractEglDrmBackend *backend;
|
||||
if (gpu->useEglStreams()) {
|
||||
#if HAVE_EGL_STREAMS
|
||||
backend = new EglStreamBackend(m_platform, gpu);
|
||||
#endif
|
||||
} else {
|
||||
#if HAVE_GBM
|
||||
backend = new EglGbmBackend(m_platform, gpu);
|
||||
AbstractEglDrmBackend *backend= new EglGbmBackend(m_platform, gpu);
|
||||
if (m_initialized) {
|
||||
backend->init();
|
||||
}
|
||||
m_backends.append(backend);
|
||||
#endif
|
||||
}
|
||||
if (backend) {
|
||||
if (m_initialized) {
|
||||
backend->init();
|
||||
}
|
||||
m_backends.append(backend);
|
||||
}
|
||||
}
|
||||
|
||||
void EglMultiBackend::removeGpu(DrmGpu *gpu)
|
||||
|
|
|
@ -1,783 +0,0 @@
|
|||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2019 NVIDIA Inc.
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#include "egl_stream_backend.h"
|
||||
#include "basiceglsurfacetexture_internal.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 "options.h"
|
||||
#include "renderloop_p.h"
|
||||
#include "scene.h"
|
||||
#include "screens.h"
|
||||
#include "surfaceitem_wayland.h"
|
||||
#include "wayland_server.h"
|
||||
#include <kwinglplatform.h>
|
||||
#include <kwingltexture.h>
|
||||
#include "drm_gpu.h"
|
||||
#include "dumb_swapchain.h"
|
||||
#include "kwineglutils_p.h"
|
||||
#include "shadowbuffer.h"
|
||||
#include "drm_pipeline.h"
|
||||
|
||||
#include <QOpenGLContext>
|
||||
#include <KWaylandServer/clientbuffer.h>
|
||||
#include <KWaylandServer/display.h>
|
||||
#include <KWaylandServer/eglstream_controller_interface.h>
|
||||
#include <drm_fourcc.h>
|
||||
|
||||
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 *drmBackend, DrmGpu *gpu)
|
||||
: AbstractEglDrmBackend(drmBackend, gpu)
|
||||
{
|
||||
}
|
||||
|
||||
EglStreamBackend::~EglStreamBackend()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void EglStreamBackend::cleanupSurfaces()
|
||||
{
|
||||
for (auto it = m_outputs.begin(); it != m_outputs.end(); ++it) {
|
||||
cleanupOutput(*it);
|
||||
}
|
||||
m_outputs.clear();
|
||||
}
|
||||
|
||||
void EglStreamBackend::cleanupOutput(Output &o)
|
||||
{
|
||||
if (o.eglSurface != EGL_NO_SURFACE) {
|
||||
eglDestroySurface(eglDisplay(), o.eglSurface);
|
||||
}
|
||||
if (o.eglStream != EGL_NO_STREAM_KHR) {
|
||||
pEglDestroyStreamKHR(eglDisplay(), o.eglStream);
|
||||
}
|
||||
o.shadowBuffer = nullptr;
|
||||
}
|
||||
|
||||
bool EglStreamBackend::initializeEgl()
|
||||
{
|
||||
initClientExtensions();
|
||||
EGLDisplay display = m_gpu->eglDisplay();
|
||||
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<EGLDeviceEXT> devices(numDevices);
|
||||
eglQueryDevicesEXT(numDevices, devices.data(), &numDevices);
|
||||
for (EGLDeviceEXT device : devices) {
|
||||
const char *drmDeviceFile = eglQueryDeviceStringEXT(device, EGL_DRM_DEVICE_FILE_EXT);
|
||||
if (m_gpu->devNode().compare(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_gpu->fd(),
|
||||
EGL_NONE
|
||||
};
|
||||
display = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, device, platformAttribs);
|
||||
break;
|
||||
}
|
||||
m_gpu->setEglDisplay(display);
|
||||
}
|
||||
|
||||
if (display == EGL_NO_DISPLAY) {
|
||||
setFailed("No suitable EGL device found");
|
||||
return false;
|
||||
}
|
||||
|
||||
setEglDisplay(display);
|
||||
if (!initEglAPI()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const QVector<QByteArray> 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(KWaylandServer::SurfaceInterface *surface)
|
||||
{
|
||||
auto it = m_streamTextures.find(surface);
|
||||
return it != m_streamTextures.end() ?
|
||||
&it.value() :
|
||||
nullptr;
|
||||
}
|
||||
|
||||
void EglStreamBackend::destroyStreamTexture(KWaylandServer::SurfaceInterface *surface)
|
||||
{
|
||||
const StreamTexture &st = m_streamTextures.take(surface);
|
||||
pEglDestroyStreamKHR(eglDisplay(), st.stream);
|
||||
glDeleteTextures(1, &st.texture);
|
||||
}
|
||||
|
||||
void EglStreamBackend::attachStreamConsumer(KWaylandServer::SurfaceInterface *surface,
|
||||
void *eglStream,
|
||||
wl_array *attribs)
|
||||
{
|
||||
makeCurrent();
|
||||
QVector<EGLAttrib> 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:" << getEglErrorString();
|
||||
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, &KWaylandServer::SurfaceInterface::destroyed, this,
|
||||
[surface, this]() {
|
||||
makeCurrent();
|
||||
destroyStreamTexture(surface);
|
||||
});
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture);
|
||||
if (!pEglStreamConsumerGLTextureExternalKHR(eglDisplay(), stream)) {
|
||||
qCWarning(KWIN_DRM) << "Failed to bind EGL stream to texture:" << getEglErrorString();
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
|
||||
}
|
||||
|
||||
void EglStreamBackend::init()
|
||||
{
|
||||
if (!m_gpu->atomicModeSetting()) {
|
||||
setFailed("EGLStream backend requires atomic modesetting");
|
||||
return;
|
||||
}
|
||||
|
||||
if (isPrimary()) {
|
||||
if (!initializeEgl()) {
|
||||
setFailed("Failed to initialize EGL api");
|
||||
return;
|
||||
}
|
||||
if (!initRenderingContext()) {
|
||||
setFailed("Failed to initialize rendering context");
|
||||
return;
|
||||
}
|
||||
|
||||
initKWinGL();
|
||||
setSupportsBufferAge(false);
|
||||
initWayland();
|
||||
|
||||
using namespace KWaylandServer;
|
||||
m_eglStreamControllerInterface = new EglStreamControllerInterface(waylandServer()->display());
|
||||
connect(m_eglStreamControllerInterface, &EglStreamControllerInterface::streamConsumerAttached, this,
|
||||
&EglStreamBackend::attachStreamConsumer);
|
||||
} else {
|
||||
// secondary NVidia GPUs only import dumb buffers
|
||||
const auto outputs = m_gpu->outputs();
|
||||
for (DrmAbstractOutput *drmOutput : outputs) {
|
||||
addOutput(drmOutput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool EglStreamBackend::initRenderingContext()
|
||||
{
|
||||
initBufferConfigs();
|
||||
|
||||
if (!createContext()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto outputs = m_gpu->outputs();
|
||||
for (DrmAbstractOutput *drmOutput : outputs) {
|
||||
addOutput(drmOutput);
|
||||
}
|
||||
return !m_outputs.isEmpty() && makeContextCurrent(m_outputs.first());
|
||||
}
|
||||
|
||||
bool EglStreamBackend::resetOutput(Output &o)
|
||||
{
|
||||
const auto &drmOutput = o.output;
|
||||
QSize sourceSize = drmOutput->sourceSize();
|
||||
|
||||
if (isPrimary()) {
|
||||
// dumb buffer used for modesetting
|
||||
o.buffer = QSharedPointer<DrmDumbBuffer>::create(m_gpu, sourceSize);
|
||||
o.targetPlane = drmOutput->pipeline()->pending.crtc->primaryPlane();
|
||||
|
||||
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:" << getEglErrorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
EGLAttrib outputAttribs[3];
|
||||
outputAttribs[0] = EGL_DRM_PLANE_EXT;
|
||||
outputAttribs[1] = o.targetPlane->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, sourceSize.width(),
|
||||
EGL_HEIGHT, sourceSize.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:" << getEglErrorString();
|
||||
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;
|
||||
|
||||
if (drmOutput->needsSoftwareTransformation()) {
|
||||
makeContextCurrent(o);
|
||||
o.shadowBuffer = QSharedPointer<ShadowBuffer>::create(o.output->pixelSize());
|
||||
if (!o.shadowBuffer->isComplete()) {
|
||||
cleanupOutput(o);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
o.dumbSwapchain = QSharedPointer<DumbSwapchain>::create(m_gpu, sourceSize);
|
||||
if (o.dumbSwapchain->isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EglStreamBackend::addOutput(DrmAbstractOutput *output)
|
||||
{
|
||||
Q_ASSERT(output->gpu() == m_gpu);
|
||||
DrmOutput *drmOutput = qobject_cast<DrmOutput *>(output);
|
||||
if (drmOutput) {
|
||||
Output o;
|
||||
o.output = drmOutput;
|
||||
if (!resetOutput(o)) {
|
||||
return false;
|
||||
}
|
||||
if (!isPrimary() && !renderingBackend()->addOutput(drmOutput)) {
|
||||
return false;
|
||||
}
|
||||
m_outputs.insert(output, o);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void EglStreamBackend::removeOutput(DrmAbstractOutput *drmOutput)
|
||||
{
|
||||
Q_ASSERT(drmOutput->gpu() == m_gpu);
|
||||
auto it = std::find_if(m_outputs.begin(), m_outputs.end(),
|
||||
[drmOutput] (const Output &o) {
|
||||
return o.output == drmOutput;
|
||||
}
|
||||
);
|
||||
if (it == m_outputs.end()) {
|
||||
return;
|
||||
}
|
||||
cleanupOutput(*it);
|
||||
m_outputs.erase(it);
|
||||
if (!isPrimary()) {
|
||||
renderingBackend()->removeOutput(drmOutput);
|
||||
}
|
||||
}
|
||||
|
||||
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:" << getEglErrorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
EGLint error = eglGetError();
|
||||
if (error != EGL_SUCCESS) {
|
||||
qCWarning(KWIN_DRM) << "Error occurred while making EGL context current:" << getEglErrorString(error);
|
||||
return false;
|
||||
}
|
||||
|
||||
const QSize size = output.output->pixelSize();
|
||||
glViewport(0, 0, size.width(), size.height());
|
||||
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:" << getEglErrorString();
|
||||
return false;
|
||||
}
|
||||
if (count == 0) {
|
||||
qCCritical(KWIN_DRM) << "No suitable EGL config found";
|
||||
return false;
|
||||
}
|
||||
|
||||
setConfig(config);
|
||||
return true;
|
||||
}
|
||||
|
||||
SurfaceTexture *EglStreamBackend::createSurfaceTextureInternal(SurfacePixmapInternal *pixmap)
|
||||
{
|
||||
return new BasicEGLSurfaceTextureInternal(this, pixmap);
|
||||
}
|
||||
|
||||
SurfaceTexture *EglStreamBackend::createSurfaceTextureWayland(SurfacePixmapWayland *pixmap)
|
||||
{
|
||||
return new EglStreamSurfaceTextureWayland(this, pixmap);
|
||||
}
|
||||
|
||||
bool EglStreamBackend::needsReset(const Output &o) const
|
||||
{
|
||||
if (o.targetPlane != o.output->pipeline()->pending.crtc->primaryPlane()) {
|
||||
return true;
|
||||
}
|
||||
QSize surfaceSize = o.dumbSwapchain ? o.dumbSwapchain->size() : o.buffer->size();
|
||||
if (surfaceSize != o.output->sourceSize()) {
|
||||
return true;
|
||||
}
|
||||
bool needsTexture = o.output->needsSoftwareTransformation();
|
||||
if (needsTexture) {
|
||||
return !o.shadowBuffer || o.shadowBuffer->textureSize() != o.output->pixelSize();
|
||||
} else {
|
||||
return o.shadowBuffer != nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
QRegion EglStreamBackend::beginFrame(AbstractOutput *drmOutput)
|
||||
{
|
||||
Q_ASSERT(m_outputs.contains(drmOutput));
|
||||
Output &o = m_outputs[drmOutput];
|
||||
if (isPrimary()) {
|
||||
if (needsReset(o)) {
|
||||
if (!resetOutput(o)) {
|
||||
// handle this better?
|
||||
return {};
|
||||
}
|
||||
}
|
||||
makeContextCurrent(o);
|
||||
if (o.shadowBuffer) {
|
||||
o.shadowBuffer->bind();
|
||||
}
|
||||
return o.output->geometry();
|
||||
} else {
|
||||
return renderingBackend()->beginFrameForSecondaryGpu(o.output);
|
||||
}
|
||||
}
|
||||
|
||||
void EglStreamBackend::endFrame(AbstractOutput *output, const QRegion &renderedRegion, const QRegion &damagedRegion)
|
||||
{
|
||||
Q_ASSERT(m_outputs.contains(output));
|
||||
Q_UNUSED(renderedRegion);
|
||||
|
||||
Output &renderOutput = m_outputs[output];
|
||||
bool frameFailed = false;
|
||||
|
||||
QSharedPointer<DrmDumbBuffer> buffer;
|
||||
if (isPrimary()) {
|
||||
buffer = renderOutput.buffer;
|
||||
if (renderOutput.shadowBuffer) {
|
||||
renderOutput.shadowBuffer->render(renderOutput.output);
|
||||
}
|
||||
if (!eglSwapBuffers(eglDisplay(), renderOutput.eglSurface)) {
|
||||
qCCritical(KWIN_DRM) << "eglSwapBuffers() failed:" << getEglErrorString();
|
||||
frameFailed = true;
|
||||
}
|
||||
} else {
|
||||
if (!renderingBackend()->swapBuffers(static_cast<DrmOutput*>(output), damagedRegion.intersected(output->geometry()))) {
|
||||
qCCritical(KWIN_DRM) << "swapping buffers on render backend for" << output << "failed!";
|
||||
frameFailed = true;
|
||||
}
|
||||
buffer = renderOutput.dumbSwapchain->acquireBuffer();
|
||||
if (!frameFailed && !renderingBackend()->exportFramebuffer(static_cast<DrmOutput*>(output), buffer->data(), buffer->size(), buffer->stride())) {
|
||||
qCCritical(KWIN_DRM) << "importing framebuffer from render backend for" << output << "failed!";
|
||||
frameFailed = true;
|
||||
}
|
||||
}
|
||||
if (!frameFailed && !renderOutput.output->present(buffer, damagedRegion)) {
|
||||
frameFailed = true;
|
||||
}
|
||||
|
||||
if (frameFailed) {
|
||||
RenderLoopPrivate *renderLoopPrivate = RenderLoopPrivate::get(output->renderLoop());
|
||||
renderLoopPrivate->notifyFrameFailed();
|
||||
} else if (isPrimary()) {
|
||||
EGLAttrib acquireAttribs[] = {
|
||||
EGL_DRM_FLIP_EVENT_DATA_NV, (EGLAttrib)output,
|
||||
EGL_NONE,
|
||||
};
|
||||
if (!pEglStreamConsumerAcquireAttribNV(eglDisplay(), renderOutput.eglStream, acquireAttribs)) {
|
||||
qCWarning(KWIN_DRM) << "Failed to acquire output EGL stream frame:" << getEglErrorString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QSharedPointer<DrmBuffer> EglStreamBackend::renderTestFrame(DrmAbstractOutput *drmOutput)
|
||||
{
|
||||
Q_ASSERT(m_outputs.contains(drmOutput));
|
||||
auto &output = m_outputs[drmOutput];
|
||||
auto buffer = output.dumbSwapchain ? output.dumbSwapchain->currentBuffer() : output.buffer;
|
||||
auto size = drmOutput->sourceSize();
|
||||
if (buffer->size() == size) {
|
||||
return buffer;
|
||||
} else {
|
||||
return QSharedPointer<DrmDumbBuffer>::create(m_gpu, size);
|
||||
}
|
||||
}
|
||||
|
||||
bool EglStreamBackend::hasOutput(AbstractOutput *output) const
|
||||
{
|
||||
return m_outputs.contains(output);
|
||||
}
|
||||
|
||||
uint32_t EglStreamBackend::drmFormat() const
|
||||
{
|
||||
return DRM_FORMAT_XRGB8888;
|
||||
}
|
||||
|
||||
/************************************************
|
||||
* EglTexture
|
||||
************************************************/
|
||||
|
||||
EglStreamSurfaceTextureWayland::EglStreamSurfaceTextureWayland(EglStreamBackend *backend,
|
||||
SurfacePixmapWayland *pixmap)
|
||||
: BasicEGLSurfaceTextureWayland(backend, pixmap)
|
||||
, m_backend(backend)
|
||||
{
|
||||
}
|
||||
|
||||
EglStreamSurfaceTextureWayland::~EglStreamSurfaceTextureWayland()
|
||||
{
|
||||
glDeleteRenderbuffers(1, &m_rbo);
|
||||
glDeleteFramebuffers(1, &m_fbo);
|
||||
glDeleteTextures(1, &m_textureId);
|
||||
}
|
||||
|
||||
bool EglStreamSurfaceTextureWayland::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:" << getEglErrorString();
|
||||
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:" << getEglErrorString();
|
||||
}
|
||||
}
|
||||
|
||||
// Re-use previous texture contents if no new frame is available
|
||||
// or if acquisition fails for some reason
|
||||
return false;
|
||||
}
|
||||
|
||||
void EglStreamSurfaceTextureWayland::createFbo()
|
||||
{
|
||||
glDeleteRenderbuffers(1, &m_rbo);
|
||||
glDeleteFramebuffers(1, &m_fbo);
|
||||
|
||||
GLuint oldReadFbo = 0;
|
||||
GLuint oldDrawFbo = 0;
|
||||
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, reinterpret_cast<GLint *>(&oldReadFbo));
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, reinterpret_cast<GLint *>(&oldDrawFbo));
|
||||
|
||||
glGenFramebuffers(1, &m_fbo);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
|
||||
glGenRenderbuffers(1, &m_rbo);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, m_rbo);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, m_format, m_texture->width(), m_texture->height());
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_rbo);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, oldReadFbo);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oldDrawFbo);
|
||||
}
|
||||
|
||||
// Renders the contents of the given EXTERNAL_OES texture
|
||||
// to the scratch framebuffer, then copies this to m_texture
|
||||
void EglStreamSurfaceTextureWayland::copyExternalTexture(GLuint tex)
|
||||
{
|
||||
GLint oldViewport[4], oldProgram, oldReadFbo, oldDrawFbo;
|
||||
glGetIntegerv(GL_VIEWPORT, oldViewport);
|
||||
glViewport(0, 0, m_texture->width(), m_texture->height());
|
||||
glGetIntegerv(GL_CURRENT_PROGRAM, &oldProgram);
|
||||
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, reinterpret_cast<GLint *>(&oldReadFbo));
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, reinterpret_cast<GLint *>(&oldDrawFbo));
|
||||
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_texture->target(), 0, m_format, 0, 0, m_texture->width(), m_texture->height(), 0);
|
||||
texture()->unbind();
|
||||
|
||||
glDisable(GL_TEXTURE_EXTERNAL_OES);
|
||||
glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, oldReadFbo);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oldDrawFbo);
|
||||
glUseProgram(oldProgram);
|
||||
glViewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]);
|
||||
}
|
||||
|
||||
bool EglStreamSurfaceTextureWayland::attachBuffer(KWaylandServer::ClientBuffer *buffer)
|
||||
{
|
||||
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);
|
||||
|
||||
return oldFormat != m_format || wasYInverted != texture()->isYInverted();
|
||||
}
|
||||
|
||||
bool EglStreamSurfaceTextureWayland::checkBuffer(KWaylandServer::SurfaceInterface *surface,
|
||||
KWaylandServer::ClientBuffer *buffer)
|
||||
{
|
||||
EGLAttrib attribs[] = {
|
||||
EGL_WAYLAND_EGLSTREAM_WL, (EGLAttrib)buffer->resource(),
|
||||
EGL_NONE
|
||||
};
|
||||
EGLStreamKHR stream = pEglCreateStreamAttribNV(m_backend->eglDisplay(), attribs);
|
||||
if (stream == EGL_NO_STREAM_KHR) {
|
||||
// eglCreateStreamAttribNV generates EGL_BAD_ACCESS if the
|
||||
// provided buffer is not a wl_eglstream. In that case, clean up
|
||||
// the old stream and fall back to the dmabuf or shm attach
|
||||
// paths.
|
||||
EGLint err = eglGetError();
|
||||
if (err == EGL_BAD_ACCESS) {
|
||||
m_backend->destroyStreamTexture(surface);
|
||||
return false;
|
||||
}
|
||||
// Otherwise it should have generated EGL_BAD_STREAM_KHR since
|
||||
// we've already created an EGLStream for it.
|
||||
Q_ASSERT(err == EGL_BAD_STREAM_KHR);
|
||||
} else {
|
||||
// If eglCreateStreamAttribNV *didn't* fail, that means the
|
||||
// buffer is a wl_eglstream but it hasn't been attached to a
|
||||
// consumer for some reason. Not much we can do here.
|
||||
qCCritical(KWIN_DRM) << "Untracked wl_eglstream attached to surface";
|
||||
pEglDestroyStreamKHR(m_backend->eglDisplay(), stream);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EglStreamSurfaceTextureWayland::create()
|
||||
{
|
||||
using namespace KWaylandServer;
|
||||
SurfaceInterface *surface = m_pixmap->surface();
|
||||
const EglStreamBackend::StreamTexture *st = m_backend->lookupStreamTexture(surface);
|
||||
if (m_pixmap->buffer() && st != nullptr && checkBuffer(surface, m_pixmap->buffer())) {
|
||||
|
||||
glGenTextures(1, &m_textureId);
|
||||
m_texture.reset(new GLTexture(m_textureId, 0, m_pixmap->buffer()->size()));
|
||||
m_texture->setWrapMode(GL_CLAMP_TO_EDGE);
|
||||
m_texture->setFilter(GL_LINEAR);
|
||||
|
||||
attachBuffer(surface->buffer());
|
||||
createFbo();
|
||||
|
||||
if (acquireStreamFrame(st->stream)) {
|
||||
copyExternalTexture(st->texture);
|
||||
if (!pEglStreamConsumerReleaseKHR(m_backend->eglDisplay(), st->stream)) {
|
||||
qCWarning(KWIN_DRM) << "Failed to release EGL stream:" << getEglErrorString();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
// Not an EGLStream surface
|
||||
return BasicEGLSurfaceTextureWayland::create();
|
||||
}
|
||||
}
|
||||
|
||||
void EglStreamSurfaceTextureWayland::update(const QRegion ®ion)
|
||||
{
|
||||
using namespace KWaylandServer;
|
||||
SurfaceInterface *surface = m_pixmap->surface();
|
||||
const EglStreamBackend::StreamTexture *st = m_backend->lookupStreamTexture(surface);
|
||||
if (m_pixmap->buffer() && st != nullptr && checkBuffer(surface, m_pixmap->buffer())) {
|
||||
|
||||
if (attachBuffer(surface->buffer())) {
|
||||
createFbo();
|
||||
}
|
||||
|
||||
if (acquireStreamFrame(st->stream)) {
|
||||
copyExternalTexture(st->texture);
|
||||
if (!pEglStreamConsumerReleaseKHR(m_backend->eglDisplay(), st->stream)) {
|
||||
qCWarning(KWIN_DRM) << "Failed to release EGL stream:" << getEglErrorString();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Not an EGLStream surface
|
||||
BasicEGLSurfaceTextureWayland::update(region);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
|
@ -1,116 +0,0 @@
|
|||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2019 NVIDIA Inc.
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#ifndef KWIN_EGL_STREAM_BACKEND_H
|
||||
#define KWIN_EGL_STREAM_BACKEND_H
|
||||
#include "abstract_egl_drm_backend.h"
|
||||
#include "basiceglsurfacetexture_wayland.h"
|
||||
#include <KWaylandServer/surface_interface.h>
|
||||
#include <KWaylandServer/eglstream_controller_interface.h>
|
||||
#include <wayland-server-core.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class DrmAbstractOutput;
|
||||
class DrmDumbBuffer;
|
||||
class DumbSwapchain;
|
||||
class ShadowBuffer;
|
||||
class DrmCrtc;
|
||||
class DrmPlane;
|
||||
|
||||
/**
|
||||
* @brief OpenGL Backend using Egl with an EGLDevice.
|
||||
*/
|
||||
class EglStreamBackend : public AbstractEglDrmBackend
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
EglStreamBackend(DrmBackend *b, DrmGpu *gpu);
|
||||
~EglStreamBackend() override;
|
||||
SurfaceTexture *createSurfaceTextureInternal(SurfacePixmapInternal *pixmap) override;
|
||||
SurfaceTexture *createSurfaceTextureWayland(SurfacePixmapWayland *pixmap) override;
|
||||
QRegion beginFrame(AbstractOutput *output) override;
|
||||
void endFrame(AbstractOutput *output, const QRegion &damage, const QRegion &damagedRegion) override;
|
||||
void init() override;
|
||||
|
||||
bool hasOutput(AbstractOutput *output) const override;
|
||||
bool addOutput(DrmAbstractOutput *output) override;
|
||||
void removeOutput(DrmAbstractOutput *output) override;
|
||||
|
||||
QSharedPointer<DrmBuffer> renderTestFrame(DrmAbstractOutput *output) override;
|
||||
uint32_t drmFormat() const override;
|
||||
|
||||
protected:
|
||||
void cleanupSurfaces() override;
|
||||
|
||||
private:
|
||||
bool initializeEgl();
|
||||
bool initBufferConfigs();
|
||||
bool initRenderingContext();
|
||||
struct StreamTexture
|
||||
{
|
||||
EGLStreamKHR stream;
|
||||
GLuint texture;
|
||||
};
|
||||
StreamTexture *lookupStreamTexture(KWaylandServer::SurfaceInterface *surface);
|
||||
void destroyStreamTexture(KWaylandServer::SurfaceInterface *surface);
|
||||
void attachStreamConsumer(KWaylandServer::SurfaceInterface *surface,
|
||||
void *eglStream,
|
||||
wl_array *attribs);
|
||||
struct Output
|
||||
{
|
||||
DrmOutput *output = nullptr;
|
||||
QSharedPointer<DrmDumbBuffer> buffer;
|
||||
EGLSurface eglSurface = EGL_NO_SURFACE;
|
||||
EGLStreamKHR eglStream = EGL_NO_STREAM_KHR;
|
||||
QSharedPointer<ShadowBuffer> shadowBuffer;
|
||||
|
||||
DrmPlane *targetPlane = nullptr;
|
||||
|
||||
// for operation as secondary GPU
|
||||
QSharedPointer<DumbSwapchain> dumbSwapchain;
|
||||
};
|
||||
bool resetOutput(Output &output);
|
||||
bool createEglSurface(Output &o);
|
||||
bool makeContextCurrent(const Output &output);
|
||||
void cleanupOutput(Output &output);
|
||||
bool needsReset(const Output &o) const;
|
||||
|
||||
QMap<AbstractOutput *, Output> m_outputs;
|
||||
KWaylandServer::EglStreamControllerInterface *m_eglStreamControllerInterface;
|
||||
QHash<KWaylandServer::SurfaceInterface *, StreamTexture> m_streamTextures;
|
||||
|
||||
friend class EglStreamSurfaceTextureWayland;
|
||||
};
|
||||
|
||||
class EglStreamSurfaceTextureWayland : public BasicEGLSurfaceTextureWayland
|
||||
{
|
||||
public:
|
||||
EglStreamSurfaceTextureWayland(EglStreamBackend *backend, SurfacePixmapWayland *pixmap);
|
||||
~EglStreamSurfaceTextureWayland() override;
|
||||
|
||||
bool create() override;
|
||||
void update(const QRegion ®ion) override;
|
||||
|
||||
private:
|
||||
bool acquireStreamFrame(EGLStreamKHR stream);
|
||||
void createFbo();
|
||||
void copyExternalTexture(GLuint tex);
|
||||
bool attachBuffer(KWaylandServer::ClientBuffer *buffer);
|
||||
bool checkBuffer(KWaylandServer::SurfaceInterface *surface,
|
||||
KWaylandServer::ClientBuffer *buffer);
|
||||
|
||||
EglStreamBackend *m_backend;
|
||||
GLuint m_fbo, m_rbo, m_textureId;
|
||||
GLenum m_format;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
|
@ -16,7 +16,6 @@
|
|||
#cmakedefine01 HAVE_X11_XINPUT
|
||||
#cmakedefine01 HAVE_GBM
|
||||
#cmakedefine01 HAVE_GBM_BO_GET_FD_FOR_PLANE
|
||||
#cmakedefine01 HAVE_EGL_STREAMS
|
||||
#cmakedefine01 HAVE_WAYLAND_EGL
|
||||
#cmakedefine01 HAVE_SYS_PRCTL_H
|
||||
#cmakedefine01 HAVE_PR_SET_DUMPABLE
|
||||
|
|
|
@ -1512,12 +1512,6 @@ QString Workspace::supportInformation() const
|
|||
support.append(yes);
|
||||
#else
|
||||
support.append(no);
|
||||
#endif
|
||||
support.append(QStringLiteral("HAVE_EGL_STREAMS: "));
|
||||
#if HAVE_EGL_STREAMS
|
||||
support.append(yes);
|
||||
#else
|
||||
support.append(no);
|
||||
#endif
|
||||
support.append(QStringLiteral("HAVE_X11_XCB: "));
|
||||
#if HAVE_X11_XCB
|
||||
|
|
Loading…
Reference in a new issue