Commit ee351568 authored by Vlad Zahorodnii's avatar Vlad Zahorodnii

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.
parent ad5f8c5c
......@@ -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());
......
......@@ -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);
......
......@@ -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";
}
}
/************************************************
......
......@@ -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;
......
......@@ -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();
}
}
}
......@@ -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);
......
......@@ -27,6 +27,7 @@ public:
void scheduleRepaint();
void maybeScheduleRepaint();
void notifyFrameFailed();
void notifyFrameCompleted(std::chrono::nanoseconds timestamp);
RenderLoop *q;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment