platforms/drm: Make frame scheduling robust

If there is a pending frame, the RenderLoop will delay all schedule
repaint requests to the next vblank event. This means that the render
loop needs to be notified when a frame has been presented or failed.

At the moment, the RenderLoop is notified only about successfully
presented frames. If some frame fails, no repaints will be scheduled
on that output.

In order to make frame scheduling robust, the RenderLoop has to be
notified if a frame has failed.
icc-effect-5.26.4
Vlad Zahorodnii 2020-11-29 13:24:38 +02:00
parent ad5f8c5c59
commit ee3515680a
7 changed files with 69 additions and 27 deletions

View File

@ -14,6 +14,7 @@
#include "gbm_surface.h"
#include "logging.h"
#include "options.h"
#include "renderloop_p.h"
#include "screens.h"
#include "drm_gpu.h"
// kwin libs
@ -589,30 +590,39 @@ void EglGbmBackend::aboutToStartPainting(int screenId, const QRegion &damagedReg
}
}
void EglGbmBackend::presentOnOutput(Output &output, const QRegion &damagedRegion)
bool EglGbmBackend::presentOnOutput(Output &output, const QRegion &damagedRegion)
{
if (isPrimary()) {
if (supportsSwapBuffersWithDamage()) {
QVector<EGLint> rects = regionToRects(output.damageHistory.constFirst(), output.output);
eglSwapBuffersWithDamageEXT(eglDisplay(), output.eglSurface,
rects.data(), rects.count()/4);
if (!eglSwapBuffersWithDamageEXT(eglDisplay(), output.eglSurface,
rects.data(), rects.count() / 4)) {
qCCritical(KWIN_DRM, "eglSwapBuffersWithDamageEXT() failed: %x", eglGetError());
return false;
}
} else {
eglSwapBuffers(eglDisplay(), output.eglSurface);
if (!eglSwapBuffers(eglDisplay(), output.eglSurface)) {
qCCritical(KWIN_DRM, "eglSwapBuffers() failed: %x", eglGetError());
return false;
}
}
output.buffer = new DrmSurfaceBuffer(m_gpu->fd(), output.gbmSurface);
} else if (output.importedGbmBo == nullptr) {
qCDebug(KWIN_DRM) << "imported gbm_bo does not exist!";
return;
return false;
} else {
output.buffer = new DrmSurfaceBuffer(m_gpu->fd(), output.importedGbmBo);
}
Q_EMIT output.output->outputChange(damagedRegion);
m_backend->present(output.buffer, output.output);
if (!m_backend->present(output.buffer, output.output)) {
return false;
}
if (supportsBufferAge()) {
eglQuerySurface(eglDisplay(), output.eglSurface, EGL_BUFFER_AGE_EXT, &output.bufferAge);
}
return true;
}
SceneOpenGLTexturePrivate *EglGbmBackend::createBackendTexture(SceneOpenGLTexture *texture)
@ -664,10 +674,19 @@ QRegion EglGbmBackend::prepareRenderingForOutput(const Output &output) const
void EglGbmBackend::endFrame(int screenId, const QRegion &renderedRegion,
const QRegion &damagedRegion)
{
Q_UNUSED(renderedRegion)
Output &output = m_outputs[screenId];
DrmOutput *drmOutput = output.output;
renderFramebufferToSurface(output);
presentOnOutput(output, damagedRegion);
if (!presentOnOutput(output, damagedRegion)) {
output.damageHistory.clear();
RenderLoopPrivate *renderLoopPrivate = RenderLoopPrivate::get(drmOutput->renderLoop());
renderLoopPrivate->notifyFrameFailed();
return;
}
if (supportsBufferAge()) {
const QRegion dirty = damagedRegion.intersected(output.output->geometry());

View File

@ -95,7 +95,7 @@ private:
void renderFramebufferToSurface(Output &output);
QRegion prepareRenderingForOutput(const Output &output) const;
void presentOnOutput(Output &output, const QRegion &damagedRegion);
bool presentOnOutput(Output &output, const QRegion &damagedRegion);
void cleanupOutput(Output &output);
void cleanupFramebuffer(Output &output);

View File

@ -15,6 +15,7 @@
#include "logging.h"
#include "logind.h"
#include "options.h"
#include "renderloop_p.h"
#include "scene.h"
#include "screens.h"
#include "wayland_server.h"
@ -438,20 +439,14 @@ bool EglStreamBackend::initBufferConfigs()
return true;
}
void EglStreamBackend::presentOnOutput(EglStreamBackend::Output &o)
bool EglStreamBackend::presentOnOutput(EglStreamBackend::Output &o)
{
eglSwapBuffers(eglDisplay(), o.eglSurface);
if (!m_backend->present(o.buffer, o.output)) {
return;
if (!eglSwapBuffers(eglDisplay(), o.eglSurface)) {
qCCritical(KWIN_DRM, "eglSwapBuffers() failed: %x", eglGetError());
return false;
}
EGLAttrib acquireAttribs[] = {
EGL_DRM_FLIP_EVENT_DATA_NV, (EGLAttrib)o.output,
EGL_NONE,
};
if (!pEglStreamConsumerAcquireAttribNV(eglDisplay(), o.eglStream, acquireAttribs)) {
qCWarning(KWIN_DRM) << "Failed to acquire output EGL stream frame";
}
return m_backend->present(o.buffer, o.output);
}
SceneOpenGLTexturePrivate *EglStreamBackend::createBackendTexture(SceneOpenGLTexture *texture)
@ -470,8 +465,23 @@ void EglStreamBackend::endFrame(int screenId, const QRegion &renderedRegion, con
{
Q_UNUSED(renderedRegion);
Q_UNUSED(damagedRegion);
Output &o = m_outputs[screenId];
presentOnOutput(o);
Output &renderOutput = m_outputs[screenId];
DrmOutput *drmOutput = renderOutput.output;
if (!presentOnOutput(renderOutput)) {
RenderLoopPrivate *renderLoopPrivate = RenderLoopPrivate::get(drmOutput->renderLoop());
renderLoopPrivate->notifyFrameFailed();
return;
}
EGLAttrib acquireAttribs[] = {
EGL_DRM_FLIP_EVENT_DATA_NV, (EGLAttrib)drmOutput,
EGL_NONE,
};
if (!pEglStreamConsumerAcquireAttribNV(eglDisplay(), renderOutput.eglStream, acquireAttribs)) {
qCWarning(KWIN_DRM) << "Failed to acquire output EGL stream frame";
}
}
/************************************************

View File

@ -64,7 +64,7 @@ private:
};
bool resetOutput(Output &output, DrmOutput *drmOutput);
bool makeContextCurrent(const Output &output);
void presentOnOutput(Output &output);
bool presentOnOutput(Output &output);
void cleanupOutput(const Output &output);
QVector<Output> m_outputs;

View File

@ -9,8 +9,8 @@
#include "scene_qpainter_drm_backend.h"
#include "drm_backend.h"
#include "drm_output.h"
#include "logind.h"
#include "drm_gpu.h"
#include "renderloop_p.h"
namespace KWin
{
@ -110,12 +110,14 @@ void DrmQPainterBackend::endFrame(int screenId, int mask, const QRegion &damage)
{
Q_UNUSED(mask)
Q_UNUSED(damage)
if (!LogindIntegration::self()->isActiveSession()) {
return;
}
const Output &rendererOutput = m_outputs[screenId];
m_backend->present(rendererOutput.buffer[rendererOutput.index], rendererOutput.output);
DrmOutput *drmOutput = rendererOutput.output;
if (!m_backend->present(rendererOutput.buffer[rendererOutput.index], drmOutput)) {
RenderLoopPrivate *renderLoopPrivate = RenderLoopPrivate::get(drmOutput->renderLoop());
renderLoopPrivate->notifyFrameFailed();
}
}
}

View File

@ -101,6 +101,16 @@ void RenderLoopPrivate::maybeScheduleRepaint()
}
}
void RenderLoopPrivate::notifyFrameFailed()
{
Q_ASSERT(pendingFrameCount > 0);
pendingFrameCount--;
if (!inhibitCount) {
maybeScheduleRepaint();
}
}
void RenderLoopPrivate::notifyFrameCompleted(std::chrono::nanoseconds timestamp)
{
Q_ASSERT(pendingFrameCount > 0);

View File

@ -27,6 +27,7 @@ public:
void scheduleRepaint();
void maybeScheduleRepaint();
void notifyFrameFailed();
void notifyFrameCompleted(std::chrono::nanoseconds timestamp);
RenderLoop *q;