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()
|
||||
{
|
||||
if (!Test::renderNodeAvailable()) {
|
||||
QSKIP("no render node available");
|
||||
return;
|
||||
}
|
||||
qputenv("XDG_DATA_DIRS", QCoreApplication::applicationDirPath().toUtf8());
|
||||
qRegisterMetaType<KWin::Window *>();
|
||||
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
|
||||
|
|
|
@ -38,6 +38,10 @@ private Q_SLOTS:
|
|||
|
||||
void DontCrashEmptyDecorationTest::initTestCase()
|
||||
{
|
||||
if (!Test::renderNodeAvailable()) {
|
||||
QSKIP("no render node available");
|
||||
return;
|
||||
}
|
||||
qRegisterMetaType<KWin::Window *>();
|
||||
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
|
||||
QVERIFY(waylandServer()->init(s_socketName));
|
||||
|
|
|
@ -42,6 +42,10 @@ private Q_SLOTS:
|
|||
|
||||
void DontCrashNoBorder::initTestCase()
|
||||
{
|
||||
if (!Test::renderNodeAvailable()) {
|
||||
QSKIP("no render node available");
|
||||
return;
|
||||
}
|
||||
qRegisterMetaType<KWin::Window *>();
|
||||
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
|
||||
QVERIFY(waylandServer()->init(s_socketName));
|
||||
|
|
|
@ -41,6 +41,10 @@ private Q_SLOTS:
|
|||
|
||||
void DontCrashReinitializeCompositorTest::initTestCase()
|
||||
{
|
||||
if (!Test::renderNodeAvailable()) {
|
||||
QSKIP("no render node available");
|
||||
return;
|
||||
}
|
||||
qputenv("XDG_DATA_DIRS", QCoreApplication::applicationDirPath().toUtf8());
|
||||
|
||||
qRegisterMetaType<KWin::Window *>();
|
||||
|
|
|
@ -40,6 +40,10 @@ private Q_SLOTS:
|
|||
|
||||
void DesktopSwitchingAnimationTest::initTestCase()
|
||||
{
|
||||
if (!Test::renderNodeAvailable()) {
|
||||
QSKIP("no render node available");
|
||||
return;
|
||||
}
|
||||
qputenv("XDG_DATA_DIRS", QCoreApplication::applicationDirPath().toUtf8());
|
||||
|
||||
qRegisterMetaType<KWin::Window *>();
|
||||
|
|
|
@ -38,6 +38,10 @@ private Q_SLOTS:
|
|||
|
||||
void MaximizeAnimationTest::initTestCase()
|
||||
{
|
||||
if (!Test::renderNodeAvailable()) {
|
||||
QSKIP("no render node available");
|
||||
return;
|
||||
}
|
||||
qputenv("XDG_DATA_DIRS", QCoreApplication::applicationDirPath().toUtf8());
|
||||
|
||||
qRegisterMetaType<KWin::Window *>();
|
||||
|
|
|
@ -41,6 +41,10 @@ private Q_SLOTS:
|
|||
|
||||
void MinimizeAnimationTest::initTestCase()
|
||||
{
|
||||
if (!Test::renderNodeAvailable()) {
|
||||
QSKIP("no render node available");
|
||||
return;
|
||||
}
|
||||
qputenv("XDG_DATA_DIRS", QCoreApplication::applicationDirPath().toUtf8());
|
||||
|
||||
qRegisterMetaType<KWin::Window *>();
|
||||
|
|
|
@ -125,6 +125,10 @@ bool ScriptedEffectWithDebugSpy::load(const QString &name)
|
|||
|
||||
void ScriptedEffectsTest::initTestCase()
|
||||
{
|
||||
if (!Test::renderNodeAvailable()) {
|
||||
QSKIP("no render node available");
|
||||
return;
|
||||
}
|
||||
qRegisterMetaType<KWin::Window *>();
|
||||
qRegisterMetaType<KWin::Effect *>();
|
||||
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
|
||||
|
|
|
@ -46,6 +46,10 @@ private Q_SLOTS:
|
|||
|
||||
void SlidingPopupsTest::initTestCase()
|
||||
{
|
||||
if (!Test::renderNodeAvailable()) {
|
||||
QSKIP("no render node available");
|
||||
return;
|
||||
}
|
||||
qputenv("XDG_DATA_DIRS", QCoreApplication::applicationDirPath().toUtf8());
|
||||
qRegisterMetaType<KWin::Window *>();
|
||||
qRegisterMetaType<KWin::Effect *>();
|
||||
|
|
|
@ -41,6 +41,10 @@ private Q_SLOTS:
|
|||
|
||||
void ToplevelOpenCloseAnimationTest::initTestCase()
|
||||
{
|
||||
if (!Test::renderNodeAvailable()) {
|
||||
QSKIP("no render node available");
|
||||
return;
|
||||
}
|
||||
qputenv("XDG_DATA_DIRS", QCoreApplication::applicationDirPath().toUtf8());
|
||||
|
||||
qRegisterMetaType<KWin::Window *>();
|
||||
|
|
|
@ -44,6 +44,10 @@ private Q_SLOTS:
|
|||
|
||||
void WobblyWindowsShadeTest::initTestCase()
|
||||
{
|
||||
if (!Test::renderNodeAvailable()) {
|
||||
QSKIP("no render node available");
|
||||
return;
|
||||
}
|
||||
qRegisterMetaType<KWin::Window *>();
|
||||
qRegisterMetaType<KWin::Effect *>();
|
||||
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
|
||||
|
|
|
@ -38,6 +38,10 @@ void GenericSceneOpenGLTest::cleanup()
|
|||
|
||||
void GenericSceneOpenGLTest::initTestCase()
|
||||
{
|
||||
if (!Test::renderNodeAvailable()) {
|
||||
QSKIP("no render node available");
|
||||
return;
|
||||
}
|
||||
qRegisterMetaType<KWin::Window *>();
|
||||
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
|
||||
QVERIFY(waylandServer()->init(s_socketName));
|
||||
|
|
|
@ -684,6 +684,13 @@ bool lockScreen();
|
|||
*/
|
||||
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
|
||||
* 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()
|
||||
{
|
||||
if (!Test::renderNodeAvailable()) {
|
||||
QSKIP("no render node available");
|
||||
return;
|
||||
}
|
||||
qRegisterMetaType<KWin::Window *>();
|
||||
qRegisterMetaType<KWin::ElectricBorder>("ElectricBorder");
|
||||
|
||||
|
|
|
@ -134,6 +134,10 @@ private:
|
|||
|
||||
void PointerInputTest::initTestCase()
|
||||
{
|
||||
if (!Test::renderNodeAvailable()) {
|
||||
QSKIP("no render node available");
|
||||
return;
|
||||
}
|
||||
qRegisterMetaType<KWin::Window *>();
|
||||
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
|
||||
QVERIFY(waylandServer()->init(s_socketName));
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <xf86drm.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
@ -1008,6 +1009,26 @@ bool unlockScreen()
|
|||
}
|
||||
#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)
|
||||
{
|
||||
xcb_disconnect(pointer);
|
||||
|
|
|
@ -12,21 +12,85 @@
|
|||
#include "virtual_output.h"
|
||||
#include "virtual_qpainter_backend.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <gbm.h>
|
||||
#include <xf86drm.h>
|
||||
|
||||
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)
|
||||
: 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()
|
||||
{
|
||||
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()
|
||||
{
|
||||
return std::make_unique<VirtualQPainterBackend>(this);
|
||||
|
|
|
@ -9,12 +9,12 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/outputbackend.h"
|
||||
#include "utils/filedescriptor.h"
|
||||
|
||||
#include <kwin_export.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QRect>
|
||||
|
||||
struct gbm_device;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class VirtualBackend;
|
||||
|
@ -39,14 +39,13 @@ public:
|
|||
|
||||
Outputs outputs() const override;
|
||||
|
||||
QVector<CompositingType> supportedCompositors() const override
|
||||
{
|
||||
return QVector<CompositingType>{OpenGLCompositing, QPainterCompositing};
|
||||
}
|
||||
QVector<CompositingType> supportedCompositors() const override;
|
||||
|
||||
void setEglDisplay(std::unique_ptr<EglDisplay> &&display);
|
||||
EglDisplay *sceneEglDisplayObject() const override;
|
||||
|
||||
gbm_device *gbmDevice() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void virtualOutputsSet(bool countChanged);
|
||||
|
||||
|
@ -55,6 +54,8 @@ private:
|
|||
|
||||
QVector<VirtualOutput *> m_outputs;
|
||||
std::unique_ptr<EglDisplay> m_display;
|
||||
FileDescriptor m_drmFileDescriptor;
|
||||
gbm_device *m_gbmDevice = nullptr;
|
||||
};
|
||||
|
||||
} // namespace KWin
|
||||
|
|
|
@ -7,35 +7,91 @@
|
|||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#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_wayland.h"
|
||||
#include "utils/softwarevsyncmonitor.h"
|
||||
#include "virtual_backend.h"
|
||||
#include "virtual_logging.h"
|
||||
#include "virtual_output.h"
|
||||
// kwin libs
|
||||
#include "libkwineffects/kwinglutils.h"
|
||||
#include <drm_fourcc.h>
|
||||
|
||||
#ifndef EGL_PLATFORM_SURFACELESS_MESA
|
||||
#define EGL_PLATFORM_SURFACELESS_MESA 0x31DD
|
||||
#endif
|
||||
#include <drm_fourcc.h>
|
||||
|
||||
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)
|
||||
: m_backend(backend)
|
||||
, m_output(output)
|
||||
{
|
||||
}
|
||||
|
||||
VirtualEglLayer::~VirtualEglLayer() = default;
|
||||
|
||||
std::shared_ptr<GLTexture> VirtualEglLayer::texture() const
|
||||
{
|
||||
return m_texture;
|
||||
return m_current->texture();
|
||||
}
|
||||
|
||||
std::optional<OutputLayerBeginFrameInfo> VirtualEglLayer::beginFrame()
|
||||
|
@ -43,15 +99,13 @@ std::optional<OutputLayerBeginFrameInfo> VirtualEglLayer::beginFrame()
|
|||
m_backend->makeCurrent();
|
||||
|
||||
const QSize nativeSize = m_output->geometry().size() * m_output->scale();
|
||||
if (!m_texture || m_texture->size() != nativeSize) {
|
||||
m_fbo.reset();
|
||||
m_texture = std::make_unique<GLTexture>(GL_RGB8, nativeSize);
|
||||
m_texture->setContentTransform(TextureTransform::MirrorY);
|
||||
m_fbo = std::make_unique<GLFramebuffer>(m_texture.get());
|
||||
if (!m_swapchain || m_swapchain->size() != nativeSize) {
|
||||
m_swapchain = std::make_unique<VirtualEglSwapchain>(nativeSize, DRM_FORMAT_XRGB8888, m_backend);
|
||||
}
|
||||
|
||||
m_current = m_swapchain->acquire();
|
||||
return OutputLayerBeginFrameInfo{
|
||||
.renderTarget = RenderTarget(m_fbo.get()),
|
||||
.renderTarget = RenderTarget(m_current->framebuffer()),
|
||||
.repaint = infiniteRegion(),
|
||||
};
|
||||
}
|
||||
|
@ -65,8 +119,7 @@ bool VirtualEglLayer::endFrame(const QRegion &renderedRegion, const QRegion &dam
|
|||
|
||||
quint32 VirtualEglLayer::format() const
|
||||
{
|
||||
// While we are using GL_RGB8, it seems to be using 32bit pixels.
|
||||
return DRM_FORMAT_XBGR8888;
|
||||
return DRM_FORMAT_XRGB8888;
|
||||
}
|
||||
|
||||
VirtualEglBackend::VirtualEglBackend(VirtualBackend *b)
|
||||
|
@ -83,24 +136,29 @@ VirtualEglBackend::~VirtualEglBackend()
|
|||
cleanup();
|
||||
}
|
||||
|
||||
VirtualBackend *VirtualEglBackend::backend() const
|
||||
{
|
||||
return m_backend;
|
||||
}
|
||||
|
||||
bool VirtualEglBackend::initializeEgl()
|
||||
{
|
||||
initClientExtensions();
|
||||
auto display = m_backend->sceneEglDisplayObject();
|
||||
|
||||
// Use eglGetPlatformDisplayEXT() to get the display pointer
|
||||
// if the implementation supports it.
|
||||
if (!m_backend->sceneEglDisplayObject()) {
|
||||
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) {
|
||||
// first try surfaceless
|
||||
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;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
setEglDisplay(display);
|
||||
return true;
|
||||
|
|
|
@ -14,16 +14,51 @@
|
|||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class GbmGraphicsBuffer;
|
||||
class GbmGraphicsBufferAllocator;
|
||||
class VirtualBackend;
|
||||
class GLFramebuffer;
|
||||
class GLTexture;
|
||||
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
|
||||
{
|
||||
public:
|
||||
VirtualEglLayer(Output *output, VirtualEglBackend *backend);
|
||||
~VirtualEglLayer() override;
|
||||
|
||||
std::optional<OutputLayerBeginFrameInfo> beginFrame() override;
|
||||
bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override;
|
||||
|
@ -34,8 +69,8 @@ public:
|
|||
private:
|
||||
VirtualEglBackend *const m_backend;
|
||||
Output *m_output;
|
||||
std::unique_ptr<GLFramebuffer> m_fbo;
|
||||
std::shared_ptr<GLTexture> m_texture;
|
||||
std::unique_ptr<VirtualEglSwapchain> m_swapchain;
|
||||
std::shared_ptr<VirtualEglLayerBuffer> m_current;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -55,6 +90,8 @@ public:
|
|||
void present(Output *output) override;
|
||||
void init() override;
|
||||
|
||||
VirtualBackend *backend() const;
|
||||
|
||||
private:
|
||||
bool initializeEgl();
|
||||
bool initRenderingContext();
|
||||
|
|
Loading…
Reference in a new issue