Move graphics reset handling to RenderBackend

This makes the Scene less overloaded and it's needed for things such as
render layers.

In hindsight, it would be great to merge checkGraphicsReset() and
beginFrame(), e.g. make beginFrame() return the status like in QRhi or
VkSwapchain. If it's OUT_OF_DATE or something, reinitialize the
compositor.
This commit is contained in:
Vlad Zahorodnii 2021-12-31 13:51:42 +02:00
parent ca7298a325
commit 9e97c06758
8 changed files with 111 additions and 114 deletions

View file

@ -307,7 +307,6 @@ bool Compositor::setupStart()
QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software);
}
connect(m_scene, &Scene::resetCompositing, this, &Compositor::reinitialize);
Q_EMIT sceneCreated();
return true;
@ -619,8 +618,14 @@ QList<Toplevel *> Compositor::windowsToRender() const
void Compositor::composite(RenderLoop *renderLoop)
{
const auto &output = m_renderLoops[renderLoop];
if (m_backend->checkGraphicsReset()) {
qCDebug(KWIN_CORE) << "Graphics reset occurred";
KNotification::event(QStringLiteral("graphicsreset"), i18n("Desktop effects were restarted due to a graphics reset"));
reinitialize();
return;
}
const auto &output = m_renderLoops[renderLoop];
fTraceDuration("Paint (", output ? output->name() : QStringLiteral("screens"), ")");
const auto windows = windowsToRender();

View file

@ -13,7 +13,21 @@
#include "screens.h"
#include "utils.h"
#include <QElapsedTimer>
#include <epoxy/gl.h>
#include <unistd.h>
// HACK: workaround for libepoxy < 1.3
#ifndef GL_GUILTY_CONTEXT_RESET
#define GL_GUILTY_CONTEXT_RESET 0x8253
#endif
#ifndef GL_INNOCENT_CONTEXT_RESET
#define GL_INNOCENT_CONTEXT_RESET 0x8254
#endif
#ifndef GL_UNKNOWN_CONTEXT_RESET
#define GL_UNKNOWN_CONTEXT_RESET 0x8255
#endif
namespace KWin
{
@ -97,4 +111,36 @@ SurfaceTexture *OpenGLBackend::createSurfaceTextureWayland(SurfacePixmapWayland
return nullptr;
}
bool OpenGLBackend::checkGraphicsReset()
{
const GLenum status = glGetGraphicsResetStatus();
if (Q_LIKELY(status == GL_NO_ERROR)) {
return false;
}
switch (status) {
case GL_GUILTY_CONTEXT_RESET:
qCDebug(KWIN_OPENGL) << "A graphics reset attributable to the current GL context occurred.";
break;
case GL_INNOCENT_CONTEXT_RESET:
qCDebug(KWIN_OPENGL) << "A graphics reset not attributable to the current GL context occurred.";
break;
case GL_UNKNOWN_CONTEXT_RESET:
qCDebug(KWIN_OPENGL) << "A graphics reset of an unknown cause occurred.";
break;
default:
break;
}
QElapsedTimer timer;
timer.start();
// Wait until the reset is completed or max 10 seconds
while (timer.elapsed() < 10000 && glGetGraphicsResetStatus() != GL_NO_ERROR) {
usleep(50);
}
return true;
}
}

View file

@ -49,6 +49,7 @@ public:
virtual void init() = 0;
CompositingType compositingType() const override final;
bool checkGraphicsReset() override final;
virtual SurfaceTexture *createSurfaceTextureInternal(SurfacePixmapInternal *pixmap);
virtual SurfaceTexture *createSurfaceTextureX11(SurfacePixmapX11 *pixmap);

View file

@ -19,4 +19,9 @@ OverlayWindow *RenderBackend::overlayWindow() const
return nullptr;
}
bool RenderBackend::checkGraphicsReset()
{
return false;
}
} // namespace KWin

View file

@ -29,6 +29,8 @@ public:
virtual CompositingType compositingType() const = 0;
virtual OverlayWindow *overlayWindow() const;
virtual bool checkGraphicsReset();
virtual QRegion beginFrame(AbstractOutput *output) = 0;
virtual void endFrame(AbstractOutput *output, const QRegion &renderedRegion, const QRegion &damagedRegion) = 0;
};

View file

@ -193,7 +193,6 @@ public:
Q_SIGNALS:
void frameRendered();
void resetCompositing();
public Q_SLOTS:
// a window has been closed

View file

@ -37,7 +37,6 @@
#include <cmath>
#include <cstddef>
#include <unistd.h>
#include <QGraphicsScale>
#include <QPainter>
@ -45,23 +44,8 @@
#include <QVector2D>
#include <QVector4D>
#include <QMatrix4x4>
#include <KLocalizedString>
#include <KNotification>
#include <KProcess>
#include <QtMath>
// HACK: workaround for libepoxy < 1.3
#ifndef GL_GUILTY_CONTEXT_RESET
#define GL_GUILTY_CONTEXT_RESET 0x8253
#endif
#ifndef GL_INNOCENT_CONTEXT_RESET
#define GL_INNOCENT_CONTEXT_RESET 0x8254
#endif
#ifndef GL_UNKNOWN_CONTEXT_RESET
#define GL_UNKNOWN_CONTEXT_RESET 0x8255
#endif
namespace KWin
{
@ -113,40 +97,6 @@ bool SceneOpenGL::initFailed() const
return !init_ok;
}
void SceneOpenGL::handleGraphicsReset(GLenum status)
{
switch (status) {
case GL_GUILTY_CONTEXT_RESET:
qCDebug(KWIN_OPENGL) << "A graphics reset attributable to the current GL context occurred.";
break;
case GL_INNOCENT_CONTEXT_RESET:
qCDebug(KWIN_OPENGL) << "A graphics reset not attributable to the current GL context occurred.";
break;
case GL_UNKNOWN_CONTEXT_RESET:
qCDebug(KWIN_OPENGL) << "A graphics reset of an unknown cause occurred.";
break;
default:
break;
}
QElapsedTimer timer;
timer.start();
// Wait until the reset is completed or max 10 seconds
while (timer.elapsed() < 10000 && glGetGraphicsResetStatus() != GL_NO_ERROR)
usleep(50);
qCDebug(KWIN_OPENGL) << "Attempting to reset compositing.";
QMetaObject::invokeMethod(this, "resetCompositing", Qt::QueuedConnection);
KNotification::event(QStringLiteral("graphicsreset"), i18n("Desktop effects were restarted due to a graphics reset"));
m_resetOccurred = true;
}
/**
* Render cursor texture in case hardware cursor is disabled.
* Useful for screen recording apps or backends that can't do planes.
@ -237,10 +187,6 @@ static SurfaceItem *findTopMostSurface(SurfaceItem *item)
void SceneOpenGL::paint(AbstractOutput *output, const QRegion &damage, const QList<Toplevel *> &toplevels,
RenderLoop *renderLoop)
{
if (m_resetOccurred) {
return; // A graphics reset has occurred, do nothing.
}
painted_screen = output;
// actually paint the frame, flushed with the NEXT frame
createStackingOrder(toplevels);
@ -258,71 +204,66 @@ void SceneOpenGL::paint(AbstractOutput *output, const QRegion &damage, const QLi
scaling = 1;
}
const GLenum status = glGetGraphicsResetStatus();
if (status != GL_NO_ERROR) {
handleGraphicsReset(status);
} else {
renderLoop->beginFrame();
renderLoop->beginFrame();
SurfaceItem *fullscreenSurface = nullptr;
for (int i = stacking_order.count() - 1; i >=0; i--) {
Window *window = stacking_order[i];
Toplevel *toplevel = window->window();
if (output && toplevel->isOnOutput(output) && window->isVisible() && toplevel->opacity() > 0) {
AbstractClient *c = dynamic_cast<AbstractClient*>(toplevel);
if (!c || !c->isFullScreen()) {
break;
}
if (!window->surfaceItem()) {
break;
}
SurfaceItem *topMost = findTopMostSurface(window->surfaceItem());
auto pixmap = topMost->pixmap();
if (!pixmap) {
break;
}
pixmap->update();
// the subsurface has to be able to cover the whole window
if (topMost->position() != QPoint(0, 0)) {
break;
}
// and it has to be completely opaque
if (!window->isOpaque() && !topMost->opaque().contains(QRect(0, 0, window->width(), window->height()))) {
break;
}
fullscreenSurface = topMost;
SurfaceItem *fullscreenSurface = nullptr;
for (int i = stacking_order.count() - 1; i >=0; i--) {
Window *window = stacking_order[i];
Toplevel *toplevel = window->window();
if (output && toplevel->isOnOutput(output) && window->isVisible() && toplevel->opacity() > 0) {
AbstractClient *c = dynamic_cast<AbstractClient*>(toplevel);
if (!c || !c->isFullScreen()) {
break;
}
if (!window->surfaceItem()) {
break;
}
SurfaceItem *topMost = findTopMostSurface(window->surfaceItem());
auto pixmap = topMost->pixmap();
if (!pixmap) {
break;
}
pixmap->update();
// the subsurface has to be able to cover the whole window
if (topMost->position() != QPoint(0, 0)) {
break;
}
// and it has to be completely opaque
if (!window->isOpaque() && !topMost->opaque().contains(QRect(0, 0, window->width(), window->height()))) {
break;
}
fullscreenSurface = topMost;
break;
}
renderLoop->setFullscreenSurface(fullscreenSurface);
}
renderLoop->setFullscreenSurface(fullscreenSurface);
bool directScanout = false;
if (m_backend->directScanoutAllowed(output) && !static_cast<EffectsHandlerImpl*>(effects)->blocksDirectScanout()) {
directScanout = m_backend->scanout(output, fullscreenSurface);
}
if (directScanout) {
renderLoop->endFrame();
} else {
// prepare rendering makescontext current on the output
repaint = m_backend->beginFrame(output);
GLVertexBuffer::streamingBuffer()->beginFrame();
bool directScanout = false;
if (m_backend->directScanoutAllowed(output) && !static_cast<EffectsHandlerImpl*>(effects)->blocksDirectScanout()) {
directScanout = m_backend->scanout(output, fullscreenSurface);
}
if (directScanout) {
renderLoop->endFrame();
} else {
// prepare rendering makescontext current on the output
repaint = m_backend->beginFrame(output);
GLVertexBuffer::streamingBuffer()->beginFrame();
GLVertexBuffer::setVirtualScreenGeometry(geo);
GLRenderTarget::setVirtualScreenGeometry(geo);
GLVertexBuffer::setVirtualScreenScale(scaling);
GLRenderTarget::setVirtualScreenScale(scaling);
GLVertexBuffer::setVirtualScreenGeometry(geo);
GLRenderTarget::setVirtualScreenGeometry(geo);
GLVertexBuffer::setVirtualScreenScale(scaling);
GLRenderTarget::setVirtualScreenScale(scaling);
updateProjectionMatrix(geo);
updateProjectionMatrix(geo);
paintScreen(damage.intersected(geo), repaint, &update, &valid,
renderLoop, projectionMatrix()); // call generic implementation
paintCursor(output, valid);
paintScreen(damage.intersected(geo), repaint, &update, &valid,
renderLoop, projectionMatrix()); // call generic implementation
paintCursor(output, valid);
renderLoop->endFrame();
renderLoop->endFrame();
GLVertexBuffer::streamingBuffer()->endOfFrame();
m_backend->endFrame(output, valid, update);
}
GLVertexBuffer::streamingBuffer()->endOfFrame();
m_backend->endFrame(output, valid, update);
}
// do cleanup

View file

@ -77,10 +77,8 @@ private:
void doPaintBackground(const QVector< float >& vertices);
void updateProjectionMatrix(const QRect &geometry);
void performPaintWindow(EffectWindowImpl* w, int mask, const QRegion &region, WindowPaintData& data);
void handleGraphicsReset(GLenum status);
bool init_ok = true;
bool m_resetOccurred = false;
OpenGLBackend *m_backend;
LanczosFilter *m_lanczosFilter = nullptr;
QScopedPointer<GLTexture> m_cursorTexture;