Implement direct scanout for the gbm backend
parent
66f87138b1
commit
30464e5c8b
|
@ -128,4 +128,18 @@ RenderLoop *AbstractOutput::renderLoop() const
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void AbstractOutput::inhibitDirectScanout()
|
||||
{
|
||||
m_directScanoutCount++;
|
||||
}
|
||||
void AbstractOutput::uninhibitDirectScanout()
|
||||
{
|
||||
m_directScanoutCount--;
|
||||
}
|
||||
|
||||
bool AbstractOutput::directScanoutInhibited() const
|
||||
{
|
||||
return m_directScanoutCount;
|
||||
}
|
||||
|
||||
} // namespace KWin
|
||||
|
|
|
@ -190,6 +190,11 @@ public:
|
|||
*/
|
||||
virtual RenderLoop *renderLoop() const;
|
||||
|
||||
void inhibitDirectScanout();
|
||||
void uninhibitDirectScanout();
|
||||
|
||||
bool directScanoutInhibited() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* This signal is emitted when the geometry of this output has changed.
|
||||
|
@ -202,6 +207,7 @@ Q_SIGNALS:
|
|||
|
||||
private:
|
||||
Q_DISABLE_COPY(AbstractOutput)
|
||||
int m_directScanoutCount = 0;
|
||||
};
|
||||
|
||||
} // namespace KWin
|
||||
|
|
|
@ -374,4 +374,19 @@ QMatrix4x4 AbstractWaylandOutput::logicalToNativeMatrix(const QRect &rect, qreal
|
|||
return matrix;
|
||||
}
|
||||
|
||||
void AbstractWaylandOutput::recordingStarted()
|
||||
{
|
||||
m_recorders++;
|
||||
}
|
||||
|
||||
void AbstractWaylandOutput::recordingStopped()
|
||||
{
|
||||
m_recorders--;
|
||||
}
|
||||
|
||||
bool AbstractWaylandOutput::isBeingRecorded()
|
||||
{
|
||||
return m_recorders;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -121,6 +121,11 @@ public:
|
|||
*/
|
||||
static QMatrix4x4 logicalToNativeMatrix(const QRect &rect, qreal scale, Transform transform);
|
||||
|
||||
void recordingStarted();
|
||||
void recordingStopped();
|
||||
|
||||
bool isBeingRecorded();
|
||||
|
||||
Q_SIGNALS:
|
||||
void modeChanged();
|
||||
void outputChange(const QRegion &damagedRegion);
|
||||
|
@ -174,6 +179,7 @@ private:
|
|||
|
||||
QString m_name;
|
||||
bool m_internal = false;
|
||||
int m_recorders = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
11
effects.cpp
11
effects.cpp
|
@ -1507,6 +1507,17 @@ QStringList EffectsHandlerImpl::activeEffects() const
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool EffectsHandlerImpl::blocksDirectScanout() const
|
||||
{
|
||||
for(QVector< KWin::EffectPair >::const_iterator it = loaded_effects.constBegin(),
|
||||
end = loaded_effects.constEnd(); it != end; ++it) {
|
||||
if (it->second->isActive() && it->second->blocksDirectScanout()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
KWaylandServer::Display *EffectsHandlerImpl::waylandDisplay() const
|
||||
{
|
||||
if (waylandServer()) {
|
||||
|
|
|
@ -203,6 +203,11 @@ public:
|
|||
QList<EffectWindow*> elevatedWindows() const;
|
||||
QStringList activeEffects() const;
|
||||
|
||||
/**
|
||||
* @returns whether or not any effect is currently active where KWin should not use direct scanout
|
||||
*/
|
||||
bool blocksDirectScanout() const;
|
||||
|
||||
/**
|
||||
* @returns Whether we are currently in a desktop rendering process triggered by paintDesktop hook
|
||||
*/
|
||||
|
|
|
@ -516,5 +516,10 @@ bool ContrastEffect::isActive() const
|
|||
return !effects->isScreenLocked();
|
||||
}
|
||||
|
||||
bool ContrastEffect::blocksDirectScanout() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace KWin
|
||||
|
||||
|
|
|
@ -51,6 +51,8 @@ public:
|
|||
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
|
||||
bool blocksDirectScanout() const override;
|
||||
|
||||
public Q_SLOTS:
|
||||
void slotWindowAdded(KWin::EffectWindow *w);
|
||||
void slotWindowDeleted(KWin::EffectWindow *w);
|
||||
|
|
|
@ -814,5 +814,10 @@ bool BlurEffect::isActive() const
|
|||
return !effects->isScreenLocked();
|
||||
}
|
||||
|
||||
bool BlurEffect::blocksDirectScanout() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace KWin
|
||||
|
||||
|
|
|
@ -54,6 +54,8 @@ public:
|
|||
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
|
||||
bool blocksDirectScanout() const override;
|
||||
|
||||
public Q_SLOTS:
|
||||
void slotWindowAdded(KWin::EffectWindow *w);
|
||||
void slotWindowDeleted(KWin::EffectWindow *w);
|
||||
|
|
|
@ -705,6 +705,11 @@ bool Effect::perform(Feature feature, const QVariantList &arguments)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Effect::blocksDirectScanout() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//****************************************
|
||||
// EffectFactory
|
||||
//****************************************
|
||||
|
|
|
@ -664,6 +664,11 @@ public:
|
|||
static void setPositionTransformations(WindowPaintData& data, QRect& region, EffectWindow* w,
|
||||
const QRect& r, Qt::AspectRatioMode aspect);
|
||||
|
||||
/**
|
||||
* overwrite this method to return false if your effect does not need to be drawn over opaque fullscreen windows
|
||||
*/
|
||||
virtual bool blocksDirectScanout() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
virtual bool borderActivated(ElectricBorder border);
|
||||
|
||||
|
|
|
@ -64,6 +64,13 @@ OverlayWindow* OpenGLBackend::overlayWindow() const
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
bool OpenGLBackend::scanout(int screenId, KWaylandServer::SurfaceInterface *surface)
|
||||
{
|
||||
Q_UNUSED(screenId)
|
||||
Q_UNUSED(surface)
|
||||
return false;
|
||||
}
|
||||
|
||||
void OpenGLBackend::copyPixels(const QRegion ®ion)
|
||||
{
|
||||
const int height = screens()->size().height();
|
||||
|
@ -89,4 +96,11 @@ void OpenGLBackend::aboutToStartPainting(int screenId, const QRegion &damage)
|
|||
Q_UNUSED(damage)
|
||||
}
|
||||
|
||||
|
||||
bool OpenGLBackend::directScanoutAllowed(int screen) const
|
||||
{
|
||||
Q_UNUSED(screen);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,6 +14,11 @@
|
|||
|
||||
#include <kwin_export.h>
|
||||
|
||||
namespace KWaylandServer
|
||||
{
|
||||
class SurfaceInterface;
|
||||
}
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class AbstractOutput;
|
||||
|
@ -60,6 +65,11 @@ public:
|
|||
virtual void doneCurrent() = 0;
|
||||
virtual QRegion beginFrame(int screenId) = 0;
|
||||
virtual void endFrame(int screenId, const QRegion &damage, const QRegion &damagedRegion) = 0;
|
||||
/**
|
||||
* Tries to directly scan out a surface to the screen)
|
||||
* @return if the scanout fails (or is not supported on the specified screen)
|
||||
*/
|
||||
virtual bool scanout(int screenId, KWaylandServer::SurfaceInterface *surface);
|
||||
|
||||
/**
|
||||
* @brief Returns the OverlayWindow used by the backend.
|
||||
|
@ -115,6 +125,7 @@ public:
|
|||
{
|
||||
return m_haveNativeFence;
|
||||
}
|
||||
virtual bool directScanoutAllowed(int screen) const;
|
||||
|
||||
/**
|
||||
* Returns the damage that has accumulated since a buffer of the given age was presented.
|
||||
|
|
|
@ -15,8 +15,9 @@
|
|||
namespace KWin
|
||||
{
|
||||
|
||||
class DrmBuffer
|
||||
class DrmBuffer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
DrmBuffer(int fd);
|
||||
virtual ~DrmBuffer() = default;
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include <xf86drm.h>
|
||||
#include <xf86drmMode.h>
|
||||
#include <gbm.h>
|
||||
// KWaylandServer
|
||||
#include "KWaylandServer/buffer_interface.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
@ -41,10 +43,15 @@ DrmSurfaceBuffer::DrmSurfaceBuffer(int fd, const std::shared_ptr<GbmSurface> &su
|
|||
gbm_bo_set_user_data(m_bo, this, nullptr);
|
||||
}
|
||||
|
||||
DrmSurfaceBuffer::DrmSurfaceBuffer(int fd, gbm_bo *buffer)
|
||||
DrmSurfaceBuffer::DrmSurfaceBuffer(int fd, gbm_bo *buffer, KWaylandServer::BufferInterface *bufferInterface)
|
||||
: DrmBuffer(fd)
|
||||
, m_bo(buffer)
|
||||
, m_bufferInterface(bufferInterface)
|
||||
{
|
||||
if (m_bufferInterface) {
|
||||
m_bufferInterface->ref();
|
||||
connect(m_bufferInterface, &KWaylandServer::BufferInterface::aboutToBeDestroyed, this, &DrmSurfaceBuffer::clearBufferInterface);
|
||||
}
|
||||
m_size = QSize(gbm_bo_get_width(m_bo), gbm_bo_get_height(m_bo));
|
||||
if (drmModeAddFB(fd, m_size.width(), m_size.height(), 24, 32, gbm_bo_get_stride(m_bo), gbm_bo_get_handle(m_bo).u32, &m_bufferId) != 0) {
|
||||
qCWarning(KWIN_DRM) << "drmModeAddFB failed";
|
||||
|
@ -58,16 +65,24 @@ DrmSurfaceBuffer::~DrmSurfaceBuffer()
|
|||
drmModeRmFB(fd(), m_bufferId);
|
||||
}
|
||||
releaseGbm();
|
||||
if (m_bufferInterface) {
|
||||
clearBufferInterface();
|
||||
}
|
||||
}
|
||||
|
||||
void DrmSurfaceBuffer::releaseGbm()
|
||||
{
|
||||
if (m_surface) {
|
||||
m_surface->releaseBuffer(m_bo);
|
||||
} else if (m_bo) {
|
||||
gbm_bo_destroy(m_bo);
|
||||
}
|
||||
m_bo = nullptr;
|
||||
}
|
||||
|
||||
void DrmSurfaceBuffer::clearBufferInterface()
|
||||
{
|
||||
disconnect(m_bufferInterface, &KWaylandServer::BufferInterface::aboutToBeDestroyed, this, &DrmSurfaceBuffer::clearBufferInterface);
|
||||
m_bufferInterface->unref();
|
||||
m_bufferInterface = nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,6 +16,11 @@
|
|||
|
||||
struct gbm_bo;
|
||||
|
||||
namespace KWaylandServer
|
||||
{
|
||||
class BufferInterface;
|
||||
}
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
|
@ -25,7 +30,7 @@ class DrmSurfaceBuffer : public DrmBuffer
|
|||
{
|
||||
public:
|
||||
DrmSurfaceBuffer(int fd, const std::shared_ptr<GbmSurface> &surface);
|
||||
DrmSurfaceBuffer(int fd, gbm_bo *buffer);
|
||||
DrmSurfaceBuffer(int fd, gbm_bo *buffer, KWaylandServer::BufferInterface *bufferInterface);
|
||||
~DrmSurfaceBuffer() override;
|
||||
|
||||
bool needsModeChange(DrmBuffer *b) const override {
|
||||
|
@ -49,6 +54,9 @@ public:
|
|||
private:
|
||||
std::shared_ptr<GbmSurface> m_surface;
|
||||
gbm_bo *m_bo = nullptr;
|
||||
KWaylandServer::BufferInterface *m_bufferInterface = nullptr;
|
||||
|
||||
void clearBufferInterface();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "renderloop_p.h"
|
||||
#include "screens.h"
|
||||
#include "drm_gpu.h"
|
||||
#include "linux_dmabuf.h"
|
||||
// kwin libs
|
||||
#include <kwinglplatform.h>
|
||||
#include <kwineglimagetexture.h>
|
||||
|
@ -24,6 +25,12 @@
|
|||
#include <gbm.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
// kwayland server
|
||||
#include "KWaylandServer/surface_interface.h"
|
||||
#include "KWaylandServer/buffer_interface.h"
|
||||
#include "KWaylandServer/linuxdmabuf_v1_interface.h"
|
||||
#include <egl_dmabuf.h>
|
||||
#include <drm_fourcc.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
@ -64,8 +71,8 @@ void EglGbmBackend::cleanupOutput(Output &output)
|
|||
if (output.secondaryGbmBo) {
|
||||
output.gbmSurface.get()->releaseBuffer(output.secondaryGbmBo);
|
||||
}
|
||||
if (output.importedGbmBo) {
|
||||
gbm_bo_destroy(output.importedGbmBo);
|
||||
if (output.directScanoutBuffer) {
|
||||
gbm_bo_destroy(output.directScanoutBuffer);
|
||||
}
|
||||
if (output.dmabufFd) {
|
||||
close(output.dmabufFd);
|
||||
|
@ -446,8 +453,10 @@ void EglGbmBackend::renderFramebufferToSurface(Output &output)
|
|||
if (!importedBuffer) {
|
||||
qCDebug(KWIN_DRM) << "failed to import dma-buf!" << strerror(errno);
|
||||
} else {
|
||||
// this buffer automatically gets destroyed by the DrmSurfaceBuffer class
|
||||
output.importedGbmBo = importedBuffer;
|
||||
if (output.directScanoutBuffer) {
|
||||
gbm_bo_destroy(output.directScanoutBuffer);
|
||||
}
|
||||
output.directScanoutBuffer = importedBuffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -570,7 +579,9 @@ void EglGbmBackend::aboutToStartPainting(int screenId, const QRegion &damagedReg
|
|||
|
||||
bool EglGbmBackend::presentOnOutput(Output &output, const QRegion &damagedRegion)
|
||||
{
|
||||
if (isPrimary()) {
|
||||
if (output.directScanoutBuffer) {
|
||||
output.buffer = new DrmSurfaceBuffer(m_gpu->fd(), output.directScanoutBuffer, output.bufferInterface);
|
||||
} else if (isPrimary()) {
|
||||
if (supportsSwapBuffersWithDamage()) {
|
||||
QVector<EGLint> rects = regionToRects(output.damageHistory.constFirst(), output.output);
|
||||
if (!eglSwapBuffersWithDamageEXT(eglDisplay(), output.eglSurface,
|
||||
|
@ -585,11 +596,9 @@ bool EglGbmBackend::presentOnOutput(Output &output, const QRegion &damagedRegion
|
|||
}
|
||||
}
|
||||
output.buffer = new DrmSurfaceBuffer(m_gpu->fd(), output.gbmSurface);
|
||||
} else if (output.importedGbmBo == nullptr) {
|
||||
} else {
|
||||
qCDebug(KWIN_DRM) << "imported gbm_bo does not exist!";
|
||||
return false;
|
||||
} else {
|
||||
output.buffer = new DrmSurfaceBuffer(m_gpu->fd(), output.importedGbmBo);
|
||||
}
|
||||
|
||||
Q_EMIT output.output->outputChange(damagedRegion);
|
||||
|
@ -621,18 +630,25 @@ void EglGbmBackend::setViewport(const Output &output) const
|
|||
QRegion EglGbmBackend::beginFrame(int screenId)
|
||||
{
|
||||
if (isPrimary()) {
|
||||
return prepareRenderingForOutput(m_outputs.at(screenId));
|
||||
return prepareRenderingForOutput(m_outputs[screenId]);
|
||||
} else {
|
||||
return renderingBackend()->beginFrameForSecondaryGpu(m_outputs.at(screenId).output);
|
||||
}
|
||||
}
|
||||
|
||||
QRegion EglGbmBackend::prepareRenderingForOutput(const Output &output) const
|
||||
QRegion EglGbmBackend::prepareRenderingForOutput(Output &output) const
|
||||
{
|
||||
makeContextCurrent(output);
|
||||
prepareRenderFramebuffer(output);
|
||||
setViewport(output);
|
||||
|
||||
if (output.directScanoutBuffer) {
|
||||
gbm_bo_destroy(output.directScanoutBuffer);
|
||||
output.directScanoutBuffer = nullptr;
|
||||
output.surfaceInterface = nullptr;
|
||||
output.bufferInterface = nullptr;
|
||||
}
|
||||
|
||||
if (supportsBufferAge()) {
|
||||
QRegion region;
|
||||
|
||||
|
@ -675,6 +691,71 @@ void EglGbmBackend::endFrame(int screenId, const QRegion &renderedRegion,
|
|||
}
|
||||
}
|
||||
|
||||
bool EglGbmBackend::scanout(int screenId, KWaylandServer::SurfaceInterface *surface)
|
||||
{
|
||||
if (!surface || !surface->buffer() || !surface->buffer()->linuxDmabufBuffer()) {
|
||||
return false;
|
||||
}
|
||||
auto buffer = surface->buffer();
|
||||
Output output = m_outputs[screenId];
|
||||
if (buffer->linuxDmabufBuffer()->size() != output.output->modeSize()) {
|
||||
return false;
|
||||
}
|
||||
EglDmabufBuffer *dmabuf = static_cast<EglDmabufBuffer*>(buffer->linuxDmabufBuffer());
|
||||
if (!dmabuf || !dmabuf->planes().count() ||
|
||||
!gbm_device_is_format_supported(m_gpu->gbmDevice(), dmabuf->format(), GBM_BO_USE_SCANOUT)) {
|
||||
return false;
|
||||
}
|
||||
gbm_bo *importedBuffer;
|
||||
if (dmabuf->planes()[0].modifier != DRM_FORMAT_MOD_INVALID
|
||||
|| dmabuf->planes()[0].offset > 0
|
||||
|| dmabuf->planes().size() > 1) {
|
||||
gbm_import_fd_modifier_data data = {};
|
||||
data.format = dmabuf->format();
|
||||
data.width = (uint32_t) dmabuf->size().width();
|
||||
data.height = (uint32_t) dmabuf->size().height();
|
||||
data.num_fds = dmabuf->planes().count();
|
||||
data.modifier = dmabuf->planes()[0].modifier;
|
||||
for (int i = 0; i < dmabuf->planes().count(); i++) {
|
||||
auto plane = dmabuf->planes()[i];
|
||||
data.fds[i] = plane.fd;
|
||||
data.offsets[i] = plane.offset;
|
||||
data.strides[i] = plane.stride;
|
||||
}
|
||||
importedBuffer = gbm_bo_import(m_gpu->gbmDevice(), GBM_BO_IMPORT_FD_MODIFIER, &data, GBM_BO_USE_SCANOUT);
|
||||
} else {
|
||||
auto plane = dmabuf->planes()[0];
|
||||
gbm_import_fd_data data = {};
|
||||
data.fd = plane.fd;
|
||||
data.width = (uint32_t) dmabuf->size().width();
|
||||
data.height = (uint32_t) dmabuf->size().height();
|
||||
data.stride = plane.stride;
|
||||
data.format = dmabuf->format();
|
||||
importedBuffer = gbm_bo_import(m_gpu->gbmDevice(), GBM_BO_IMPORT_FD, &data, GBM_BO_USE_SCANOUT);
|
||||
}
|
||||
if (!importedBuffer) {
|
||||
qCDebug(KWIN_DRM) << "importing the dmabuf for direct scanout failed:" << strerror(errno);
|
||||
return false;
|
||||
}
|
||||
// damage tracking for screen casting
|
||||
QRegion damage;
|
||||
if (output.surfaceInterface == surface) {
|
||||
QRegion trackedDamage = surface->trackedDamage();
|
||||
surface->resetTrackedDamage();
|
||||
for (const auto &rect : trackedDamage) {
|
||||
auto damageRect = QRect(rect);
|
||||
damageRect.translate(output.output->geometry().topLeft());
|
||||
damage |= damageRect;
|
||||
}
|
||||
} else {
|
||||
damage = output.output->geometry();
|
||||
}
|
||||
output.directScanoutBuffer = importedBuffer;
|
||||
output.surfaceInterface = surface;
|
||||
output.bufferInterface = buffer;
|
||||
return presentOnOutput(output, damage);
|
||||
}
|
||||
|
||||
QSharedPointer<GLTexture> EglGbmBackend::textureForOutput(AbstractOutput *abstractOutput) const
|
||||
{
|
||||
const QVector<KWin::EglGbmBackend::Output>::const_iterator itOutput = std::find_if(m_outputs.begin(), m_outputs.end(),
|
||||
|
@ -693,7 +774,8 @@ QSharedPointer<GLTexture> EglGbmBackend::textureForOutput(AbstractOutput *abstra
|
|||
return glTexture;
|
||||
}
|
||||
|
||||
EGLImageKHR image = eglCreateImageKHR(eglDisplay(), nullptr, EGL_NATIVE_PIXMAP_KHR, itOutput->buffer->getBo(), nullptr);
|
||||
EGLImageKHR image = eglCreateImageKHR(eglDisplay(), nullptr, EGL_NATIVE_PIXMAP_KHR,
|
||||
itOutput->directScanoutBuffer ? itOutput->directScanoutBuffer : itOutput->buffer->getBo(), nullptr);
|
||||
if (image == EGL_NO_IMAGE_KHR) {
|
||||
qCWarning(KWIN_DRM) << "Failed to record frame: Error creating EGLImageKHR - " << glGetError();
|
||||
return {};
|
||||
|
@ -702,6 +784,11 @@ QSharedPointer<GLTexture> EglGbmBackend::textureForOutput(AbstractOutput *abstra
|
|||
return QSharedPointer<EGLImageTexture>::create(eglDisplay(), image, GL_RGBA8, drmOutput->modeSize());
|
||||
}
|
||||
|
||||
bool EglGbmBackend::directScanoutAllowed(int screen) const
|
||||
{
|
||||
return !m_outputs[screen].output->directScanoutInhibited();
|
||||
}
|
||||
|
||||
/************************************************
|
||||
* EglTexture
|
||||
************************************************/
|
||||
|
|
|
@ -15,6 +15,11 @@
|
|||
struct gbm_surface;
|
||||
struct gbm_bo;
|
||||
|
||||
namespace KWaylandServer
|
||||
{
|
||||
class BufferInterface;
|
||||
}
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class AbstractOutput;
|
||||
|
@ -36,6 +41,7 @@ public:
|
|||
QRegion beginFrame(int screenId) override;
|
||||
void endFrame(int screenId, const QRegion &damage, const QRegion &damagedRegion) override;
|
||||
void init() override;
|
||||
bool scanout(int screenId, KWaylandServer::SurfaceInterface *surface) override;
|
||||
|
||||
QSharedPointer<GLTexture> textureForOutput(AbstractOutput *requestedOutput) const override;
|
||||
|
||||
|
@ -48,6 +54,8 @@ public:
|
|||
int getDmabufForSecondaryGpuOutput(AbstractOutput *output, uint32_t *format, uint32_t *stride) override;
|
||||
QRegion beginFrameForSecondaryGpu(AbstractOutput *output) override;
|
||||
|
||||
bool directScanoutAllowed(int screen) const override;
|
||||
|
||||
protected:
|
||||
void cleanupSurfaces() override;
|
||||
void aboutToStartPainting(int screenId, const QRegion &damage) override;
|
||||
|
@ -76,7 +84,9 @@ private:
|
|||
|
||||
int dmabufFd = 0;
|
||||
gbm_bo *secondaryGbmBo = nullptr;
|
||||
gbm_bo *importedGbmBo = nullptr;
|
||||
gbm_bo *directScanoutBuffer = nullptr;
|
||||
KWaylandServer::SurfaceInterface *surfaceInterface = nullptr;
|
||||
KWaylandServer::BufferInterface *bufferInterface = nullptr;
|
||||
};
|
||||
|
||||
bool resetOutput(Output &output, DrmOutput *drmOutput);
|
||||
|
@ -90,7 +100,7 @@ private:
|
|||
|
||||
void prepareRenderFramebuffer(const Output &output) const;
|
||||
void renderFramebufferToSurface(Output &output);
|
||||
QRegion prepareRenderingForOutput(const Output &output) const;
|
||||
QRegion prepareRenderingForOutput(Output &output) const;
|
||||
|
||||
bool presentOnOutput(Output &output, const QRegion &damagedRegion);
|
||||
|
||||
|
|
|
@ -72,6 +72,14 @@ void EglMultiBackend::endFrame(int screenId, const QRegion &damage, const QRegio
|
|||
backend->endFrame(internalScreenId, damage, damagedRegion);
|
||||
}
|
||||
|
||||
bool EglMultiBackend::scanout(int screenId, KWaylandServer::SurfaceInterface *surface)
|
||||
{
|
||||
int internalScreenId;
|
||||
AbstractEglBackend *backend = findBackend(screenId, internalScreenId);
|
||||
Q_ASSERT(backend != nullptr);
|
||||
return backend->scanout(internalScreenId, surface);
|
||||
}
|
||||
|
||||
bool EglMultiBackend::makeCurrent()
|
||||
{
|
||||
return m_backends[0]->makeCurrent();
|
||||
|
@ -104,7 +112,7 @@ void EglMultiBackend::screenGeometryChanged(const QSize &size)
|
|||
Q_UNUSED(size)
|
||||
}
|
||||
|
||||
AbstractEglDrmBackend *EglMultiBackend::findBackend(int screenId, int& internalScreenId)
|
||||
AbstractEglDrmBackend *EglMultiBackend::findBackend(int screenId, int& internalScreenId) const
|
||||
{
|
||||
int screens = 0;
|
||||
for (int i = 0; i < m_backends.count(); i++) {
|
||||
|
@ -123,4 +131,12 @@ void EglMultiBackend::addBackend(AbstractEglDrmBackend *backend)
|
|||
m_backends.append(backend);
|
||||
}
|
||||
|
||||
bool EglMultiBackend::directScanoutAllowed(int screenId) const
|
||||
{
|
||||
int internalScreenId;
|
||||
AbstractEglBackend *backend = findBackend(screenId, internalScreenId);
|
||||
Q_ASSERT(backend != nullptr);
|
||||
return backend->directScanoutAllowed(internalScreenId);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ public:
|
|||
|
||||
QRegion beginFrame(int screenId) override;
|
||||
void endFrame(int screenId, const QRegion &damage, const QRegion &damagedRegion) override;
|
||||
bool scanout(int screenId, KWaylandServer::SurfaceInterface *surface) override;
|
||||
|
||||
bool makeCurrent() override;
|
||||
void doneCurrent() override;
|
||||
|
@ -36,10 +37,12 @@ public:
|
|||
|
||||
void addBackend(AbstractEglDrmBackend *backend);
|
||||
|
||||
bool directScanoutAllowed(int screen) const override;
|
||||
|
||||
private:
|
||||
QVector<AbstractEglDrmBackend*> m_backends;
|
||||
|
||||
AbstractEglDrmBackend *findBackend(int screenId, int& internalScreenId);
|
||||
AbstractEglDrmBackend *findBackend(int screenId, int& internalScreenId) const;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -618,9 +618,6 @@ void SceneOpenGL::paint(int screenId, const QRegion &damage, const QList<Topleve
|
|||
QRegion repaint;
|
||||
QRect geo;
|
||||
qreal scaling;
|
||||
|
||||
// prepare rendering makes context current on the output
|
||||
repaint = m_backend->beginFrame(screenId);
|
||||
if (screenId != -1) {
|
||||
geo = screens()->geometry(screenId);
|
||||
scaling = screens()->scale(screenId);
|
||||
|
@ -629,52 +626,87 @@ void SceneOpenGL::paint(int screenId, const QRegion &damage, const QList<Topleve
|
|||
scaling = 1;
|
||||
}
|
||||
|
||||
GLVertexBuffer::setVirtualScreenGeometry(geo);
|
||||
GLRenderTarget::setVirtualScreenGeometry(geo);
|
||||
GLVertexBuffer::setVirtualScreenScale(scaling);
|
||||
GLRenderTarget::setVirtualScreenScale(scaling);
|
||||
|
||||
const GLenum status = glGetGraphicsResetStatus();
|
||||
if (status != GL_NO_ERROR) {
|
||||
handleGraphicsReset(status);
|
||||
} else {
|
||||
int mask = 0;
|
||||
updateProjectionMatrix();
|
||||
renderLoop->beginFrame();
|
||||
|
||||
paintScreen(&mask, damage.intersected(geo), repaint, &update, &valid,
|
||||
renderLoop, projectionMatrix(), geo, scaling); // call generic implementation
|
||||
paintCursor(valid);
|
||||
|
||||
if (!GLPlatform::instance()->isGLES() && screenId == -1) {
|
||||
const QSize &screenSize = screens()->size();
|
||||
const QRegion displayRegion(0, 0, screenSize.width(), screenSize.height());
|
||||
|
||||
// copy dirty parts from front to backbuffer
|
||||
if (!m_backend->supportsBufferAge() &&
|
||||
options->glPreferBufferSwap() == Options::CopyFrontBuffer &&
|
||||
valid != displayRegion) {
|
||||
glReadBuffer(GL_FRONT);
|
||||
m_backend->copyPixels(displayRegion - valid);
|
||||
glReadBuffer(GL_BACK);
|
||||
valid = displayRegion;
|
||||
bool directScanout = false;
|
||||
if (m_backend->directScanoutAllowed(screenId)) {
|
||||
EffectsHandlerImpl *implEffects = static_cast<EffectsHandlerImpl*>(effects);
|
||||
if (!implEffects->blocksDirectScanout()) {
|
||||
for (int i = stacking_order.count() - 1; i >= 0; i--) {
|
||||
Window *window = stacking_order[i];
|
||||
AbstractClient *c = dynamic_cast<AbstractClient*>(window->window());
|
||||
if (!c) {
|
||||
break;
|
||||
}
|
||||
if (c->isOnScreen(screenId)) {
|
||||
if (window->isOpaque() && c->isFullScreen()) {
|
||||
auto pixmap = window->windowPixmap<WindowPixmap>();
|
||||
if (!pixmap) {
|
||||
break;
|
||||
}
|
||||
pixmap->update();
|
||||
pixmap = pixmap->topMostSurface();
|
||||
// the subsurface has to be able to cover the whole window
|
||||
if (pixmap->position() != QPoint(0, 0)) {
|
||||
break;
|
||||
}
|
||||
directScanout = m_backend->scanout(screenId, pixmap->surface());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!directScanout) {
|
||||
|
||||
renderLoop->endFrame();
|
||||
// prepare rendering makes context current on the output
|
||||
repaint = m_backend->beginFrame(screenId);
|
||||
|
||||
GLVertexBuffer::streamingBuffer()->endOfFrame();
|
||||
m_backend->endFrame(screenId, valid, update);
|
||||
GLVertexBuffer::streamingBuffer()->framePosted();
|
||||
GLVertexBuffer::setVirtualScreenGeometry(geo);
|
||||
GLRenderTarget::setVirtualScreenGeometry(geo);
|
||||
GLVertexBuffer::setVirtualScreenScale(scaling);
|
||||
GLRenderTarget::setVirtualScreenScale(scaling);
|
||||
|
||||
if (m_currentFence) {
|
||||
if (!m_syncManager->updateFences()) {
|
||||
qCDebug(KWIN_OPENGL) << "Aborting explicit synchronization with the X command stream.";
|
||||
qCDebug(KWIN_OPENGL) << "Future frames will be rendered unsynchronized.";
|
||||
delete m_syncManager;
|
||||
m_syncManager = nullptr;
|
||||
const GLenum status = glGetGraphicsResetStatus();
|
||||
if (status != GL_NO_ERROR) {
|
||||
handleGraphicsReset(status);
|
||||
} else {
|
||||
int mask = 0;
|
||||
updateProjectionMatrix();
|
||||
renderLoop->beginFrame();
|
||||
|
||||
paintScreen(&mask, damage.intersected(geo), repaint, &update, &valid,
|
||||
renderLoop, projectionMatrix(), geo, scaling); // call generic implementation
|
||||
paintCursor(valid);
|
||||
|
||||
if (!GLPlatform::instance()->isGLES() && screenId == -1) {
|
||||
const QSize &screenSize = screens()->size();
|
||||
const QRegion displayRegion(0, 0, screenSize.width(), screenSize.height());
|
||||
|
||||
// copy dirty parts from front to backbuffer
|
||||
if (!m_backend->supportsBufferAge() &&
|
||||
options->glPreferBufferSwap() == Options::CopyFrontBuffer &&
|
||||
valid != displayRegion) {
|
||||
glReadBuffer(GL_FRONT);
|
||||
m_backend->copyPixels(displayRegion - valid);
|
||||
glReadBuffer(GL_BACK);
|
||||
valid = displayRegion;
|
||||
}
|
||||
}
|
||||
|
||||
renderLoop->endFrame();
|
||||
|
||||
GLVertexBuffer::streamingBuffer()->endOfFrame();
|
||||
m_backend->endFrame(screenId, valid, update);
|
||||
GLVertexBuffer::streamingBuffer()->framePosted();
|
||||
|
||||
if (m_currentFence) {
|
||||
if (!m_syncManager->updateFences()) {
|
||||
qCDebug(KWIN_OPENGL) << "Aborting explicit synchronization with the X command stream.";
|
||||
qCDebug(KWIN_OPENGL) << "Future frames will be rendered unsynchronized.";
|
||||
delete m_syncManager;
|
||||
m_syncManager = nullptr;
|
||||
}
|
||||
m_currentFence = nullptr;
|
||||
}
|
||||
m_currentFence = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -121,8 +121,12 @@ void ScreencastManager::streamOutput(KWaylandServer::ScreencastStreamV1Interface
|
|||
};
|
||||
connect(stream, &PipeWireStream::startStreaming, waylandStream, [streamOutput, stream, bufferToStream] {
|
||||
Compositor::self()->addRepaint(streamOutput->geometry());
|
||||
streamOutput->recordingStarted();
|
||||
connect(streamOutput, &AbstractWaylandOutput::outputChange, stream, bufferToStream);
|
||||
});
|
||||
connect(stream, &PipeWireStream::stopStreaming, waylandStream, [streamOutput]{
|
||||
streamOutput->recordingStopped();
|
||||
});
|
||||
integrateStreams(waylandStream, stream);
|
||||
}
|
||||
|
||||
|
|
|
@ -1389,6 +1389,15 @@ KWaylandServer::SurfaceInterface *WindowPixmap::surface() const
|
|||
}
|
||||
}
|
||||
|
||||
WindowPixmap *WindowPixmap::topMostSurface()
|
||||
{
|
||||
if (m_children.count() == 0) {
|
||||
return this;
|
||||
} else {
|
||||
return m_children.last()->topMostSurface();
|
||||
}
|
||||
}
|
||||
|
||||
QPoint WindowPixmap::position() const
|
||||
{
|
||||
if (subSurface())
|
||||
|
|
7
scene.h
7
scene.h
|
@ -264,14 +264,15 @@ protected:
|
|||
QRegion damaged_region;
|
||||
// The screen that is being currently painted
|
||||
int painted_screen = -1;
|
||||
|
||||
// windows in their stacking order
|
||||
QVector< Window* > stacking_order;
|
||||
private:
|
||||
void paintWindowThumbnails(Scene::Window *w, const QRegion ®ion, qreal opacity, qreal brightness, qreal saturation);
|
||||
void paintDesktopThumbnails(Scene::Window *w);
|
||||
std::chrono::milliseconds m_expectedPresentTimestamp = std::chrono::milliseconds::zero();
|
||||
void reallocRepaints();
|
||||
QHash< Toplevel*, Window* > m_windows;
|
||||
// windows in their stacking order
|
||||
QVector< Window* > stacking_order;
|
||||
QVector<QRegion> m_repaints;
|
||||
// how many times finalPaintScreen() has been called
|
||||
int m_paintScreenCount = 0;
|
||||
|
@ -571,6 +572,8 @@ public:
|
|||
*/
|
||||
KWaylandServer::SurfaceInterface *surface() const;
|
||||
|
||||
WindowPixmap *topMostSurface();
|
||||
|
||||
protected:
|
||||
explicit WindowPixmap(Scene::Window *window);
|
||||
explicit WindowPixmap(KWaylandServer::SubSurfaceInterface *subSurface, WindowPixmap *parent);
|
||||
|
|
Loading…
Reference in New Issue