Introduce DamageJournal helper

This commit is contained in:
Vlad Zahorodnii 2021-07-24 13:03:00 +03:00
parent 2fe102768a
commit 501298b0e0
9 changed files with 88 additions and 66 deletions

View file

@ -35,30 +35,6 @@ void OpenGLBackend::setFailed(const QString &reason)
m_failed = true;
}
void OpenGLBackend::addToDamageHistory(const QRegion &region)
{
if (m_damageHistory.count() > 10)
m_damageHistory.removeLast();
m_damageHistory.prepend(region);
}
QRegion OpenGLBackend::accumulatedDamageHistory(int bufferAge) const
{
QRegion region;
// Note: An age of zero means the buffer contents are undefined
if (bufferAge > 0 && bufferAge <= m_damageHistory.count()) {
for (int i = 0; i < bufferAge - 1; i++)
region |= m_damageHistory[i];
} else {
const QSize &s = screens()->size();
region = QRegion(0, 0, s.width(), s.height());
}
return region;
}
OverlayWindow* OpenGLBackend::overlayWindow() const
{
return nullptr;

View file

@ -129,16 +129,6 @@ public:
}
virtual bool directScanoutAllowed(int screen) const;
/**
* Returns the damage that has accumulated since a buffer of the given age was presented.
*/
QRegion accumulatedDamageHistory(int bufferAge) const;
/**
* Saves the given region to damage history.
*/
void addToDamageHistory(const QRegion &region);
/**
* The backend specific extensions (e.g. EGL/GLX extensions).
*
@ -244,10 +234,6 @@ private:
* @brief Whether the initialization failed, of course default to @c false.
*/
bool m_failed;
/**
* @brief The damage history for the past 10 frames.
*/
QList<QRegion> m_damageHistory;
QList<QByteArray> m_extensions;
};

View file

@ -528,20 +528,13 @@ QRegion EglGbmBackend::prepareRenderingForOutput(Output &output)
}
setViewport(output);
const QRect geometry = output.output->geometry();
if (supportsBufferAge()) {
QRegion region;
// Note: An age of zero means the buffer contents are undefined
if (output.current.bufferAge > 0 && output.current.bufferAge <= output.current.damageHistory.count()) {
for (int i = 0; i < output.current.bufferAge - 1; i++)
region |= output.current.damageHistory[i];
} else {
region = output.output->geometry();
}
return region;
auto current = &output.current;
return current->damageJournal.accumulate(current->bufferAge, geometry);
}
return output.output->geometry();
return geometry;
}
QSharedPointer<DrmBuffer> EglGbmBackend::endFrameWithBuffer(int screenId, const QRegion &dirty)
@ -581,10 +574,7 @@ void EglGbmBackend::updateBufferAge(Output &output, const QRegion &dirty)
{
if (supportsBufferAge()) {
eglQuerySurface(eglDisplay(), output.current.gbmSurface->eglSurface(), EGL_BUFFER_AGE_EXT, &output.current.bufferAge);
if (output.current.damageHistory.count() > 10) {
output.current.damageHistory.removeLast();
}
output.current.damageHistory.prepend(dirty);
output.current.damageJournal.add(dirty);
}
}
@ -665,7 +655,7 @@ bool EglGbmBackend::scanout(int screenId, SurfaceItem *surfaceItem)
// ensure that a context is current like with normal presentation
makeCurrent();
if (output.output->present(bo, damage)) {
output.current.damageHistory.clear();
output.current.damageJournal.clear();
if (output.surfaceInterface != surface) {
auto path = surface->client()->executablePath();
qCDebug(KWIN_DRM).nospace() << "Direct scanout starting on output " << output.output->name() << " for application \"" << path << "\"";

View file

@ -9,6 +9,7 @@
#ifndef KWIN_EGL_GBM_BACKEND_H
#define KWIN_EGL_GBM_BACKEND_H
#include "abstract_egl_drm_backend.h"
#include "utils.h"
#include <kwinglutils.h>
@ -87,10 +88,7 @@ private:
QSharedPointer<ShadowBuffer> shadowBuffer;
QSharedPointer<GbmSurface> gbmSurface;
int bufferAge = 0;
/**
* @brief The damage history for the past 10 frames.
*/
QList<QRegion> damageHistory;
DamageJournal damageJournal;
// for secondary GPU import
ImportMode importMode = ImportMode::Dmabuf;

View file

@ -98,8 +98,9 @@ QRegion EglBackend::beginFrame(int screenId)
glViewport(0, 0, size.width(), size.height());
QRegion repaint;
if (supportsBufferAge())
repaint = accumulatedDamageHistory(m_bufferAge);
if (supportsBufferAge()) {
repaint = m_damageJournal.accumulate(m_bufferAge, screens()->geometry());
}
eglWaitNative(EGL_CORE_NATIVE_ENGINE);
@ -122,7 +123,7 @@ void EglBackend::endFrame(int screenId, const QRegion &renderedRegion, const QRe
// Save the damaged region to history
if (supportsBufferAge()) {
addToDamageHistory(damagedRegion);
m_damageJournal.add(damagedRegion);
}
}

View file

@ -40,6 +40,7 @@ private:
X11StandalonePlatform *m_backend;
SoftwareVsyncMonitor *m_vsyncMonitor;
DamageJournal m_damageJournal;
int m_bufferAge = 0;
};

View file

@ -746,8 +746,9 @@ QRegion GlxBackend::beginFrame(int screenId)
const QSize size = screens()->size();
glViewport(0, 0, size.width(), size.height());
if (supportsBufferAge())
repaint = accumulatedDamageHistory(m_bufferAge);
if (supportsBufferAge()) {
repaint = m_damageJournal.accumulate(m_bufferAge, screens()->geometry());
}
glXWaitX();
@ -770,8 +771,9 @@ void GlxBackend::endFrame(int screenId, const QRegion &renderedRegion, const QRe
overlayWindow()->show(); // since that pass may take long
// Save the damaged region to history
if (supportsBufferAge())
addToDamageHistory(damagedRegion);
if (supportsBufferAge()) {
m_damageJournal.add(damagedRegion);
}
}
void GlxBackend::vblank(std::chrono::nanoseconds timestamp)

View file

@ -106,6 +106,7 @@ private:
QHash<xcb_visualid_t, FBConfigInfo *> m_fbconfigHash;
QHash<xcb_visualid_t, int> m_visualDepthHash;
std::unique_ptr<SwapEventFilter> m_swapEventFilter;
DamageJournal m_damageJournal;
int m_bufferAge;
bool m_haveMESACopySubBuffer = false;
bool m_haveMESASwapControl = false;

View file

@ -210,6 +210,73 @@ protected:
#endif
};
/**
* The DamageJournal class is a helper that tracks last N damage regions.
*/
class KWIN_EXPORT DamageJournal
{
public:
/**
* Returns the maximum number of damage regions that can be stored in the journal.
*/
int capacity() const
{
return m_capacity;
}
/**
* Sets the maximum number of damage regions that can be stored in the journal
* to @a capacity.
*/
void setCapacity(int capacity)
{
m_capacity = capacity;
}
/**
* Adds the specified @a region to the journal.
*/
void add(const QRegion &region)
{
while (m_log.size() >= m_capacity) {
m_log.takeLast();
}
m_log.prepend(region);
}
/**
* Clears the damage journal. Typically, one would want to clear the damage journal
* if a buffer swap fails for some reason.
*/
void clear()
{
m_log.clear();
}
/**
* Accumulates the damage regions in the log up to the specified @a bufferAge.
*
* If the specified buffer age value refers to a damage region older than the last
* one in the journal, @a fallback will be returned.
*/
QRegion accumulate(int bufferAge, const QRegion &fallback = QRegion()) const
{
QRegion region;
if (bufferAge > 0 && bufferAge <= m_log.size()) {
for (int i = 0; i < bufferAge - 1; ++i) {
region |= m_log[i];
}
} else {
region = fallback;
}
return region;
}
private:
QList<QRegion> m_log;
int m_capacity = 10;
};
} // namespace
// Must be outside namespace