backends/virtual: Port to gbm
The virtual backend uses the surfaceless platform. On the other hand, we move in a direction where the graphics buffer type is explicit, which creates issues for the virtual backend. This change ports the virtual backend to gbm so we could manually allocate dmabuf buffers in order to unify buffer handling in kwin. Its main drawback is that you won't be able to use the virtual backend on setups without render nodes. On the other hand, given that the compositor is meaningless without clients being able to share buffers with it, it's reasonable to require some way to create and export prime buffers.
This commit is contained in:
parent
15f6c910be
commit
c693450976
20 changed files with 286 additions and 42 deletions
|
@ -40,6 +40,10 @@ private Q_SLOTS:
|
||||||
|
|
||||||
void DontCrashAuroraeDestroyDecoTest::initTestCase()
|
void DontCrashAuroraeDestroyDecoTest::initTestCase()
|
||||||
{
|
{
|
||||||
|
if (!Test::renderNodeAvailable()) {
|
||||||
|
QSKIP("no render node available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
qputenv("XDG_DATA_DIRS", QCoreApplication::applicationDirPath().toUtf8());
|
qputenv("XDG_DATA_DIRS", QCoreApplication::applicationDirPath().toUtf8());
|
||||||
qRegisterMetaType<KWin::Window *>();
|
qRegisterMetaType<KWin::Window *>();
|
||||||
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
|
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
|
||||||
|
|
|
@ -38,6 +38,10 @@ private Q_SLOTS:
|
||||||
|
|
||||||
void DontCrashEmptyDecorationTest::initTestCase()
|
void DontCrashEmptyDecorationTest::initTestCase()
|
||||||
{
|
{
|
||||||
|
if (!Test::renderNodeAvailable()) {
|
||||||
|
QSKIP("no render node available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
qRegisterMetaType<KWin::Window *>();
|
qRegisterMetaType<KWin::Window *>();
|
||||||
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
|
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
|
||||||
QVERIFY(waylandServer()->init(s_socketName));
|
QVERIFY(waylandServer()->init(s_socketName));
|
||||||
|
|
|
@ -42,6 +42,10 @@ private Q_SLOTS:
|
||||||
|
|
||||||
void DontCrashNoBorder::initTestCase()
|
void DontCrashNoBorder::initTestCase()
|
||||||
{
|
{
|
||||||
|
if (!Test::renderNodeAvailable()) {
|
||||||
|
QSKIP("no render node available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
qRegisterMetaType<KWin::Window *>();
|
qRegisterMetaType<KWin::Window *>();
|
||||||
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
|
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
|
||||||
QVERIFY(waylandServer()->init(s_socketName));
|
QVERIFY(waylandServer()->init(s_socketName));
|
||||||
|
|
|
@ -41,6 +41,10 @@ private Q_SLOTS:
|
||||||
|
|
||||||
void DontCrashReinitializeCompositorTest::initTestCase()
|
void DontCrashReinitializeCompositorTest::initTestCase()
|
||||||
{
|
{
|
||||||
|
if (!Test::renderNodeAvailable()) {
|
||||||
|
QSKIP("no render node available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
qputenv("XDG_DATA_DIRS", QCoreApplication::applicationDirPath().toUtf8());
|
qputenv("XDG_DATA_DIRS", QCoreApplication::applicationDirPath().toUtf8());
|
||||||
|
|
||||||
qRegisterMetaType<KWin::Window *>();
|
qRegisterMetaType<KWin::Window *>();
|
||||||
|
|
|
@ -40,6 +40,10 @@ private Q_SLOTS:
|
||||||
|
|
||||||
void DesktopSwitchingAnimationTest::initTestCase()
|
void DesktopSwitchingAnimationTest::initTestCase()
|
||||||
{
|
{
|
||||||
|
if (!Test::renderNodeAvailable()) {
|
||||||
|
QSKIP("no render node available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
qputenv("XDG_DATA_DIRS", QCoreApplication::applicationDirPath().toUtf8());
|
qputenv("XDG_DATA_DIRS", QCoreApplication::applicationDirPath().toUtf8());
|
||||||
|
|
||||||
qRegisterMetaType<KWin::Window *>();
|
qRegisterMetaType<KWin::Window *>();
|
||||||
|
|
|
@ -38,6 +38,10 @@ private Q_SLOTS:
|
||||||
|
|
||||||
void MaximizeAnimationTest::initTestCase()
|
void MaximizeAnimationTest::initTestCase()
|
||||||
{
|
{
|
||||||
|
if (!Test::renderNodeAvailable()) {
|
||||||
|
QSKIP("no render node available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
qputenv("XDG_DATA_DIRS", QCoreApplication::applicationDirPath().toUtf8());
|
qputenv("XDG_DATA_DIRS", QCoreApplication::applicationDirPath().toUtf8());
|
||||||
|
|
||||||
qRegisterMetaType<KWin::Window *>();
|
qRegisterMetaType<KWin::Window *>();
|
||||||
|
|
|
@ -41,6 +41,10 @@ private Q_SLOTS:
|
||||||
|
|
||||||
void MinimizeAnimationTest::initTestCase()
|
void MinimizeAnimationTest::initTestCase()
|
||||||
{
|
{
|
||||||
|
if (!Test::renderNodeAvailable()) {
|
||||||
|
QSKIP("no render node available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
qputenv("XDG_DATA_DIRS", QCoreApplication::applicationDirPath().toUtf8());
|
qputenv("XDG_DATA_DIRS", QCoreApplication::applicationDirPath().toUtf8());
|
||||||
|
|
||||||
qRegisterMetaType<KWin::Window *>();
|
qRegisterMetaType<KWin::Window *>();
|
||||||
|
|
|
@ -125,6 +125,10 @@ bool ScriptedEffectWithDebugSpy::load(const QString &name)
|
||||||
|
|
||||||
void ScriptedEffectsTest::initTestCase()
|
void ScriptedEffectsTest::initTestCase()
|
||||||
{
|
{
|
||||||
|
if (!Test::renderNodeAvailable()) {
|
||||||
|
QSKIP("no render node available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
qRegisterMetaType<KWin::Window *>();
|
qRegisterMetaType<KWin::Window *>();
|
||||||
qRegisterMetaType<KWin::Effect *>();
|
qRegisterMetaType<KWin::Effect *>();
|
||||||
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
|
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
|
||||||
|
|
|
@ -46,6 +46,10 @@ private Q_SLOTS:
|
||||||
|
|
||||||
void SlidingPopupsTest::initTestCase()
|
void SlidingPopupsTest::initTestCase()
|
||||||
{
|
{
|
||||||
|
if (!Test::renderNodeAvailable()) {
|
||||||
|
QSKIP("no render node available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
qputenv("XDG_DATA_DIRS", QCoreApplication::applicationDirPath().toUtf8());
|
qputenv("XDG_DATA_DIRS", QCoreApplication::applicationDirPath().toUtf8());
|
||||||
qRegisterMetaType<KWin::Window *>();
|
qRegisterMetaType<KWin::Window *>();
|
||||||
qRegisterMetaType<KWin::Effect *>();
|
qRegisterMetaType<KWin::Effect *>();
|
||||||
|
|
|
@ -41,6 +41,10 @@ private Q_SLOTS:
|
||||||
|
|
||||||
void ToplevelOpenCloseAnimationTest::initTestCase()
|
void ToplevelOpenCloseAnimationTest::initTestCase()
|
||||||
{
|
{
|
||||||
|
if (!Test::renderNodeAvailable()) {
|
||||||
|
QSKIP("no render node available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
qputenv("XDG_DATA_DIRS", QCoreApplication::applicationDirPath().toUtf8());
|
qputenv("XDG_DATA_DIRS", QCoreApplication::applicationDirPath().toUtf8());
|
||||||
|
|
||||||
qRegisterMetaType<KWin::Window *>();
|
qRegisterMetaType<KWin::Window *>();
|
||||||
|
|
|
@ -44,6 +44,10 @@ private Q_SLOTS:
|
||||||
|
|
||||||
void WobblyWindowsShadeTest::initTestCase()
|
void WobblyWindowsShadeTest::initTestCase()
|
||||||
{
|
{
|
||||||
|
if (!Test::renderNodeAvailable()) {
|
||||||
|
QSKIP("no render node available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
qRegisterMetaType<KWin::Window *>();
|
qRegisterMetaType<KWin::Window *>();
|
||||||
qRegisterMetaType<KWin::Effect *>();
|
qRegisterMetaType<KWin::Effect *>();
|
||||||
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
|
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
|
||||||
|
|
|
@ -38,6 +38,10 @@ void GenericSceneOpenGLTest::cleanup()
|
||||||
|
|
||||||
void GenericSceneOpenGLTest::initTestCase()
|
void GenericSceneOpenGLTest::initTestCase()
|
||||||
{
|
{
|
||||||
|
if (!Test::renderNodeAvailable()) {
|
||||||
|
QSKIP("no render node available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
qRegisterMetaType<KWin::Window *>();
|
qRegisterMetaType<KWin::Window *>();
|
||||||
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
|
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
|
||||||
QVERIFY(waylandServer()->init(s_socketName));
|
QVERIFY(waylandServer()->init(s_socketName));
|
||||||
|
|
|
@ -684,6 +684,13 @@ bool lockScreen();
|
||||||
*/
|
*/
|
||||||
bool unlockScreen();
|
bool unlockScreen();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns @c true if the system has at least one render node; otherwise returns @c false.
|
||||||
|
*
|
||||||
|
* This can be used to test whether the system is capable of allocating and sharing prime buffers, etc.
|
||||||
|
*/
|
||||||
|
bool renderNodeAvailable();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an X11 connection
|
* Creates an X11 connection
|
||||||
* Internally a nested event loop is spawned whilst we connect to avoid a deadlock
|
* Internally a nested event loop is spawned whilst we connect to avoid a deadlock
|
||||||
|
|
|
@ -174,6 +174,10 @@ std::pair<Window *, std::unique_ptr<KWayland::Client::Surface>> LockScreenTest::
|
||||||
|
|
||||||
void LockScreenTest::initTestCase()
|
void LockScreenTest::initTestCase()
|
||||||
{
|
{
|
||||||
|
if (!Test::renderNodeAvailable()) {
|
||||||
|
QSKIP("no render node available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
qRegisterMetaType<KWin::Window *>();
|
qRegisterMetaType<KWin::Window *>();
|
||||||
qRegisterMetaType<KWin::ElectricBorder>("ElectricBorder");
|
qRegisterMetaType<KWin::ElectricBorder>("ElectricBorder");
|
||||||
|
|
||||||
|
|
|
@ -134,6 +134,10 @@ private:
|
||||||
|
|
||||||
void PointerInputTest::initTestCase()
|
void PointerInputTest::initTestCase()
|
||||||
{
|
{
|
||||||
|
if (!Test::renderNodeAvailable()) {
|
||||||
|
QSKIP("no render node available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
qRegisterMetaType<KWin::Window *>();
|
qRegisterMetaType<KWin::Window *>();
|
||||||
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
|
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
|
||||||
QVERIFY(waylandServer()->init(s_socketName));
|
QVERIFY(waylandServer()->init(s_socketName));
|
||||||
|
|
|
@ -50,6 +50,7 @@
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <xf86drm.h>
|
||||||
|
|
||||||
namespace KWin
|
namespace KWin
|
||||||
{
|
{
|
||||||
|
@ -1008,6 +1009,26 @@ bool unlockScreen()
|
||||||
}
|
}
|
||||||
#endif // KWIN_BUILD_LOCKSCREEN
|
#endif // KWIN_BUILD_LOCKSCREEN
|
||||||
|
|
||||||
|
bool renderNodeAvailable()
|
||||||
|
{
|
||||||
|
const int deviceCount = drmGetDevices2(0, nullptr, 0);
|
||||||
|
if (deviceCount <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<drmDevice *> devices(deviceCount);
|
||||||
|
if (drmGetDevices2(0, devices.data(), devices.size()) < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto deviceCleanup = qScopeGuard([&devices]() {
|
||||||
|
drmFreeDevices(devices.data(), devices.size());
|
||||||
|
});
|
||||||
|
|
||||||
|
return std::any_of(devices.constBegin(), devices.constEnd(), [](drmDevice *device) {
|
||||||
|
return device->available_nodes & (1 << DRM_NODE_RENDER);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void XcbConnectionDeleter::operator()(xcb_connection_t *pointer)
|
void XcbConnectionDeleter::operator()(xcb_connection_t *pointer)
|
||||||
{
|
{
|
||||||
xcb_disconnect(pointer);
|
xcb_disconnect(pointer);
|
||||||
|
|
|
@ -12,21 +12,85 @@
|
||||||
#include "virtual_output.h"
|
#include "virtual_output.h"
|
||||||
#include "virtual_qpainter_backend.h"
|
#include "virtual_qpainter_backend.h"
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <gbm.h>
|
||||||
|
#include <xf86drm.h>
|
||||||
|
|
||||||
namespace KWin
|
namespace KWin
|
||||||
{
|
{
|
||||||
|
|
||||||
|
static FileDescriptor findRenderDevice()
|
||||||
|
{
|
||||||
|
const int deviceCount = drmGetDevices2(0, nullptr, 0);
|
||||||
|
if (deviceCount <= 0) {
|
||||||
|
return FileDescriptor{};
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<drmDevice *> devices(deviceCount);
|
||||||
|
if (drmGetDevices2(0, devices.data(), devices.size()) < 0) {
|
||||||
|
return FileDescriptor{};
|
||||||
|
}
|
||||||
|
auto deviceCleanup = qScopeGuard([&devices]() {
|
||||||
|
drmFreeDevices(devices.data(), devices.size());
|
||||||
|
});
|
||||||
|
|
||||||
|
for (drmDevice *device : std::as_const(devices)) {
|
||||||
|
// If it's a vgem device, prefer the primary node because gbm will attempt to allocate
|
||||||
|
// dumb buffers and they can be allocated only on the primary node.
|
||||||
|
int nodeType = DRM_NODE_RENDER;
|
||||||
|
if (device->bustype == DRM_BUS_PLATFORM) {
|
||||||
|
if (strcmp(device->businfo.platform->fullname, "vgem") == 0) {
|
||||||
|
nodeType = DRM_NODE_PRIMARY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device->available_nodes & (1 << nodeType)) {
|
||||||
|
FileDescriptor fd{open(device->nodes[nodeType], O_RDWR | O_CLOEXEC)};
|
||||||
|
if (fd.isValid()) {
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FileDescriptor{};
|
||||||
|
}
|
||||||
|
|
||||||
VirtualBackend::VirtualBackend(QObject *parent)
|
VirtualBackend::VirtualBackend(QObject *parent)
|
||||||
: OutputBackend(parent)
|
: OutputBackend(parent)
|
||||||
{
|
{
|
||||||
|
m_drmFileDescriptor = findRenderDevice();
|
||||||
|
if (m_drmFileDescriptor.isValid()) {
|
||||||
|
m_gbmDevice = gbm_create_device(m_drmFileDescriptor.get());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualBackend::~VirtualBackend() = default;
|
VirtualBackend::~VirtualBackend()
|
||||||
|
{
|
||||||
|
if (m_gbmDevice) {
|
||||||
|
gbm_device_destroy(m_gbmDevice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool VirtualBackend::initialize()
|
bool VirtualBackend::initialize()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVector<CompositingType> VirtualBackend::supportedCompositors() const
|
||||||
|
{
|
||||||
|
QVector<CompositingType> compositingTypes;
|
||||||
|
if (m_gbmDevice) {
|
||||||
|
compositingTypes.append(OpenGLCompositing);
|
||||||
|
}
|
||||||
|
compositingTypes.append(QPainterCompositing);
|
||||||
|
return compositingTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
gbm_device *VirtualBackend::gbmDevice() const
|
||||||
|
{
|
||||||
|
return m_gbmDevice;
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<QPainterBackend> VirtualBackend::createQPainterBackend()
|
std::unique_ptr<QPainterBackend> VirtualBackend::createQPainterBackend()
|
||||||
{
|
{
|
||||||
return std::make_unique<VirtualQPainterBackend>(this);
|
return std::make_unique<VirtualQPainterBackend>(this);
|
||||||
|
|
|
@ -9,12 +9,12 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "core/outputbackend.h"
|
#include "core/outputbackend.h"
|
||||||
|
#include "utils/filedescriptor.h"
|
||||||
|
|
||||||
#include <kwin_export.h>
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QRect>
|
#include <QRect>
|
||||||
|
|
||||||
|
struct gbm_device;
|
||||||
|
|
||||||
namespace KWin
|
namespace KWin
|
||||||
{
|
{
|
||||||
class VirtualBackend;
|
class VirtualBackend;
|
||||||
|
@ -39,14 +39,13 @@ public:
|
||||||
|
|
||||||
Outputs outputs() const override;
|
Outputs outputs() const override;
|
||||||
|
|
||||||
QVector<CompositingType> supportedCompositors() const override
|
QVector<CompositingType> supportedCompositors() const override;
|
||||||
{
|
|
||||||
return QVector<CompositingType>{OpenGLCompositing, QPainterCompositing};
|
|
||||||
}
|
|
||||||
|
|
||||||
void setEglDisplay(std::unique_ptr<EglDisplay> &&display);
|
void setEglDisplay(std::unique_ptr<EglDisplay> &&display);
|
||||||
EglDisplay *sceneEglDisplayObject() const override;
|
EglDisplay *sceneEglDisplayObject() const override;
|
||||||
|
|
||||||
|
gbm_device *gbmDevice() const;
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void virtualOutputsSet(bool countChanged);
|
void virtualOutputsSet(bool countChanged);
|
||||||
|
|
||||||
|
@ -55,6 +54,8 @@ private:
|
||||||
|
|
||||||
QVector<VirtualOutput *> m_outputs;
|
QVector<VirtualOutput *> m_outputs;
|
||||||
std::unique_ptr<EglDisplay> m_display;
|
std::unique_ptr<EglDisplay> m_display;
|
||||||
|
FileDescriptor m_drmFileDescriptor;
|
||||||
|
gbm_device *m_gbmDevice = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace KWin
|
} // namespace KWin
|
||||||
|
|
|
@ -7,35 +7,91 @@
|
||||||
SPDX-License-Identifier: GPL-2.0-or-later
|
SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
*/
|
*/
|
||||||
#include "virtual_egl_backend.h"
|
#include "virtual_egl_backend.h"
|
||||||
// kwin
|
#include "core/gbmgraphicsbufferallocator.h"
|
||||||
|
#include "libkwineffects/kwinglutils.h"
|
||||||
#include "platformsupport/scenes/opengl/basiceglsurfacetexture_internal.h"
|
#include "platformsupport/scenes/opengl/basiceglsurfacetexture_internal.h"
|
||||||
#include "platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.h"
|
#include "platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.h"
|
||||||
#include "utils/softwarevsyncmonitor.h"
|
#include "utils/softwarevsyncmonitor.h"
|
||||||
#include "virtual_backend.h"
|
#include "virtual_backend.h"
|
||||||
#include "virtual_logging.h"
|
#include "virtual_logging.h"
|
||||||
#include "virtual_output.h"
|
#include "virtual_output.h"
|
||||||
// kwin libs
|
|
||||||
#include "libkwineffects/kwinglutils.h"
|
|
||||||
#include <drm_fourcc.h>
|
|
||||||
|
|
||||||
#ifndef EGL_PLATFORM_SURFACELESS_MESA
|
#include <drm_fourcc.h>
|
||||||
#define EGL_PLATFORM_SURFACELESS_MESA 0x31DD
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace KWin
|
namespace KWin
|
||||||
{
|
{
|
||||||
|
|
||||||
|
VirtualEglLayerBuffer::VirtualEglLayerBuffer(GbmGraphicsBuffer *buffer, VirtualEglBackend *backend)
|
||||||
|
: m_graphicsBuffer(buffer)
|
||||||
|
{
|
||||||
|
m_texture = backend->importDmaBufAsTexture(*buffer->dmabufAttributes());
|
||||||
|
m_framebuffer = std::make_unique<GLFramebuffer>(m_texture.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualEglLayerBuffer::~VirtualEglLayerBuffer()
|
||||||
|
{
|
||||||
|
m_texture.reset();
|
||||||
|
m_framebuffer.reset();
|
||||||
|
m_graphicsBuffer->drop();
|
||||||
|
}
|
||||||
|
|
||||||
|
GbmGraphicsBuffer *VirtualEglLayerBuffer::graphicsBuffer() const
|
||||||
|
{
|
||||||
|
return m_graphicsBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLFramebuffer *VirtualEglLayerBuffer::framebuffer() const
|
||||||
|
{
|
||||||
|
return m_framebuffer.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<GLTexture> VirtualEglLayerBuffer::texture() const
|
||||||
|
{
|
||||||
|
return m_texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualEglSwapchain::VirtualEglSwapchain(const QSize &size, uint32_t format, VirtualEglBackend *backend)
|
||||||
|
: m_backend(backend)
|
||||||
|
, m_size(size)
|
||||||
|
, m_format(format)
|
||||||
|
, m_allocator(std::make_unique<GbmGraphicsBufferAllocator>(backend->backend()->gbmDevice()))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize VirtualEglSwapchain::size() const
|
||||||
|
{
|
||||||
|
return m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<VirtualEglLayerBuffer> VirtualEglSwapchain::acquire()
|
||||||
|
{
|
||||||
|
for (const auto &buffer : std::as_const(m_buffers)) {
|
||||||
|
if (!buffer->graphicsBuffer()->isReferenced()) {
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GbmGraphicsBuffer *graphicsBuffer = m_allocator->allocate(m_size, m_format);
|
||||||
|
if (!graphicsBuffer) {
|
||||||
|
qCWarning(KWIN_VIRTUAL) << "Failed to allocate layer swapchain buffer";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto buffer = std::make_shared<VirtualEglLayerBuffer>(graphicsBuffer, m_backend);
|
||||||
|
m_buffers.append(buffer);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
VirtualEglLayer::VirtualEglLayer(Output *output, VirtualEglBackend *backend)
|
VirtualEglLayer::VirtualEglLayer(Output *output, VirtualEglBackend *backend)
|
||||||
: m_backend(backend)
|
: m_backend(backend)
|
||||||
, m_output(output)
|
, m_output(output)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualEglLayer::~VirtualEglLayer() = default;
|
|
||||||
|
|
||||||
std::shared_ptr<GLTexture> VirtualEglLayer::texture() const
|
std::shared_ptr<GLTexture> VirtualEglLayer::texture() const
|
||||||
{
|
{
|
||||||
return m_texture;
|
return m_current->texture();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<OutputLayerBeginFrameInfo> VirtualEglLayer::beginFrame()
|
std::optional<OutputLayerBeginFrameInfo> VirtualEglLayer::beginFrame()
|
||||||
|
@ -43,15 +99,13 @@ std::optional<OutputLayerBeginFrameInfo> VirtualEglLayer::beginFrame()
|
||||||
m_backend->makeCurrent();
|
m_backend->makeCurrent();
|
||||||
|
|
||||||
const QSize nativeSize = m_output->geometry().size() * m_output->scale();
|
const QSize nativeSize = m_output->geometry().size() * m_output->scale();
|
||||||
if (!m_texture || m_texture->size() != nativeSize) {
|
if (!m_swapchain || m_swapchain->size() != nativeSize) {
|
||||||
m_fbo.reset();
|
m_swapchain = std::make_unique<VirtualEglSwapchain>(nativeSize, DRM_FORMAT_XRGB8888, m_backend);
|
||||||
m_texture = std::make_unique<GLTexture>(GL_RGB8, nativeSize);
|
|
||||||
m_texture->setContentTransform(TextureTransform::MirrorY);
|
|
||||||
m_fbo = std::make_unique<GLFramebuffer>(m_texture.get());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_current = m_swapchain->acquire();
|
||||||
return OutputLayerBeginFrameInfo{
|
return OutputLayerBeginFrameInfo{
|
||||||
.renderTarget = RenderTarget(m_fbo.get()),
|
.renderTarget = RenderTarget(m_current->framebuffer()),
|
||||||
.repaint = infiniteRegion(),
|
.repaint = infiniteRegion(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -65,8 +119,7 @@ bool VirtualEglLayer::endFrame(const QRegion &renderedRegion, const QRegion &dam
|
||||||
|
|
||||||
quint32 VirtualEglLayer::format() const
|
quint32 VirtualEglLayer::format() const
|
||||||
{
|
{
|
||||||
// While we are using GL_RGB8, it seems to be using 32bit pixels.
|
return DRM_FORMAT_XRGB8888;
|
||||||
return DRM_FORMAT_XBGR8888;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualEglBackend::VirtualEglBackend(VirtualBackend *b)
|
VirtualEglBackend::VirtualEglBackend(VirtualBackend *b)
|
||||||
|
@ -83,24 +136,29 @@ VirtualEglBackend::~VirtualEglBackend()
|
||||||
cleanup();
|
cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VirtualBackend *VirtualEglBackend::backend() const
|
||||||
|
{
|
||||||
|
return m_backend;
|
||||||
|
}
|
||||||
|
|
||||||
bool VirtualEglBackend::initializeEgl()
|
bool VirtualEglBackend::initializeEgl()
|
||||||
{
|
{
|
||||||
initClientExtensions();
|
initClientExtensions();
|
||||||
auto display = m_backend->sceneEglDisplayObject();
|
|
||||||
|
|
||||||
// Use eglGetPlatformDisplayEXT() to get the display pointer
|
if (!m_backend->sceneEglDisplayObject()) {
|
||||||
// if the implementation supports it.
|
for (const QByteArray &extension : {QByteArrayLiteral("EGL_EXT_platform_base"), QByteArrayLiteral("EGL_KHR_platform_gbm")}) {
|
||||||
|
if (!hasClientExtension(extension)) {
|
||||||
|
qCWarning(KWIN_VIRTUAL) << extension << "client extension is not supported by the platform";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_backend->setEglDisplay(EglDisplay::create(eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, m_backend->gbmDevice(), nullptr)));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto display = m_backend->sceneEglDisplayObject();
|
||||||
if (!display) {
|
if (!display) {
|
||||||
// first try surfaceless
|
return false;
|
||||||
if (hasClientExtension(QByteArrayLiteral("EGL_MESA_platform_surfaceless"))) {
|
|
||||||
m_backend->setEglDisplay(EglDisplay::create(eglGetPlatformDisplayEXT(EGL_PLATFORM_SURFACELESS_MESA, EGL_DEFAULT_DISPLAY, nullptr)));
|
|
||||||
display = m_backend->sceneEglDisplayObject();
|
|
||||||
} else {
|
|
||||||
qCWarning(KWIN_VIRTUAL) << "Extension EGL_MESA_platform_surfaceless not available";
|
|
||||||
}
|
|
||||||
if (!display) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
setEglDisplay(display);
|
setEglDisplay(display);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -14,16 +14,51 @@
|
||||||
|
|
||||||
namespace KWin
|
namespace KWin
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class GbmGraphicsBuffer;
|
||||||
|
class GbmGraphicsBufferAllocator;
|
||||||
class VirtualBackend;
|
class VirtualBackend;
|
||||||
class GLFramebuffer;
|
class GLFramebuffer;
|
||||||
class GLTexture;
|
class GLTexture;
|
||||||
class VirtualEglBackend;
|
class VirtualEglBackend;
|
||||||
|
|
||||||
|
class VirtualEglLayerBuffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VirtualEglLayerBuffer(GbmGraphicsBuffer *buffer, VirtualEglBackend *backend);
|
||||||
|
~VirtualEglLayerBuffer();
|
||||||
|
|
||||||
|
GbmGraphicsBuffer *graphicsBuffer() const;
|
||||||
|
GLFramebuffer *framebuffer() const;
|
||||||
|
std::shared_ptr<GLTexture> texture() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
GbmGraphicsBuffer *m_graphicsBuffer;
|
||||||
|
std::unique_ptr<GLFramebuffer> m_framebuffer;
|
||||||
|
std::shared_ptr<GLTexture> m_texture;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VirtualEglSwapchain
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VirtualEglSwapchain(const QSize &size, uint32_t format, VirtualEglBackend *backend);
|
||||||
|
|
||||||
|
QSize size() const;
|
||||||
|
|
||||||
|
std::shared_ptr<VirtualEglLayerBuffer> acquire();
|
||||||
|
|
||||||
|
private:
|
||||||
|
VirtualEglBackend *m_backend;
|
||||||
|
QSize m_size;
|
||||||
|
uint32_t m_format;
|
||||||
|
std::unique_ptr<GbmGraphicsBufferAllocator> m_allocator;
|
||||||
|
QVector<std::shared_ptr<VirtualEglLayerBuffer>> m_buffers;
|
||||||
|
};
|
||||||
|
|
||||||
class VirtualEglLayer : public OutputLayer
|
class VirtualEglLayer : public OutputLayer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
VirtualEglLayer(Output *output, VirtualEglBackend *backend);
|
VirtualEglLayer(Output *output, VirtualEglBackend *backend);
|
||||||
~VirtualEglLayer() override;
|
|
||||||
|
|
||||||
std::optional<OutputLayerBeginFrameInfo> beginFrame() override;
|
std::optional<OutputLayerBeginFrameInfo> beginFrame() override;
|
||||||
bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override;
|
bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override;
|
||||||
|
@ -34,8 +69,8 @@ public:
|
||||||
private:
|
private:
|
||||||
VirtualEglBackend *const m_backend;
|
VirtualEglBackend *const m_backend;
|
||||||
Output *m_output;
|
Output *m_output;
|
||||||
std::unique_ptr<GLFramebuffer> m_fbo;
|
std::unique_ptr<VirtualEglSwapchain> m_swapchain;
|
||||||
std::shared_ptr<GLTexture> m_texture;
|
std::shared_ptr<VirtualEglLayerBuffer> m_current;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -55,6 +90,8 @@ public:
|
||||||
void present(Output *output) override;
|
void present(Output *output) override;
|
||||||
void init() override;
|
void init() override;
|
||||||
|
|
||||||
|
VirtualBackend *backend() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool initializeEgl();
|
bool initializeEgl();
|
||||||
bool initRenderingContext();
|
bool initRenderingContext();
|
||||||
|
|
Loading…
Reference in a new issue