Implement direct scanout for the gbm backend

icc-effect-5.26.4
Xaver Hugl 2021-02-02 13:26:43 +00:00
parent 66f87138b1
commit 30464e5c8b
25 changed files with 359 additions and 65 deletions

View File

@ -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

View File

@ -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

View File

@ -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;
}
}

View File

@ -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;
};
}

View File

@ -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()) {

View File

@ -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
*/

View File

@ -516,5 +516,10 @@ bool ContrastEffect::isActive() const
return !effects->isScreenLocked();
}
bool ContrastEffect::blocksDirectScanout() const
{
return false;
}
} // namespace KWin

View File

@ -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);

View File

@ -814,5 +814,10 @@ bool BlurEffect::isActive() const
return !effects->isScreenLocked();
}
bool BlurEffect::blocksDirectScanout() const
{
return false;
}
} // namespace KWin

View File

@ -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);

View File

@ -705,6 +705,11 @@ bool Effect::perform(Feature feature, const QVariantList &arguments)
return false;
}
bool Effect::blocksDirectScanout() const
{
return true;
}
//****************************************
// EffectFactory
//****************************************

View File

@ -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);

View File

@ -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 &region)
{
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;
}
}

View File

@ -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.

View File

@ -15,8 +15,9 @@
namespace KWin
{
class DrmBuffer
class DrmBuffer : public QObject
{
Q_OBJECT
public:
DrmBuffer(int fd);
virtual ~DrmBuffer() = default;

View File

@ -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;
}
}

View File

@ -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();
};
}

View File

@ -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
************************************************/

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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;
};

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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())

View File

@ -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 &region, 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);