Support frame callback in Wayland backend
The egl wayland backend registers for the callback for a rendered frame. This allows to throttle KWin's compositor so that we don't render frames which wouldn't end up on the screen. For this the Scene provides a method to query whether the last frame got rendered. By default this returns true in all backends. The Egl Wayland backend returns true or false depending on whether the callback for the last frame was recieved. In case the last frame has not been renderd when performCompositing is tried to be called, the method returns just like in the case when the overlay window is not visible. Once the frame callback has been recieved performCompositing is invoked again.
This commit is contained in:
parent
cfa1ead9e1
commit
c9779825d1
9 changed files with 98 additions and 0 deletions
|
@ -87,6 +87,7 @@ Compositor::Compositor(QObject* workspace)
|
|||
, m_finishing(false)
|
||||
, m_timeSinceLastVBlank(0)
|
||||
, m_scene(NULL)
|
||||
, m_waitingForFrameRendered(false)
|
||||
{
|
||||
qRegisterMetaType<Compositor::SuspendReason>("Compositor::SuspendReason");
|
||||
new CompositingAdaptor(this);
|
||||
|
@ -527,10 +528,23 @@ void Compositor::timerEvent(QTimerEvent *te)
|
|||
QObject::timerEvent(te);
|
||||
}
|
||||
|
||||
void Compositor::lastFrameRendered()
|
||||
{
|
||||
if (!m_waitingForFrameRendered) {
|
||||
return;
|
||||
}
|
||||
m_waitingForFrameRendered = false;
|
||||
performCompositing();
|
||||
}
|
||||
|
||||
void Compositor::performCompositing()
|
||||
{
|
||||
if (!isOverlayWindowVisible())
|
||||
return; // nothing is visible anyway
|
||||
if (!m_scene->isLastFrameRendered()) {
|
||||
m_waitingForFrameRendered = true;
|
||||
return; // frame wouldn't make it on the screen
|
||||
}
|
||||
|
||||
// Create a list of all windows in the stacking order
|
||||
ToplevelList windows = Workspace::self()->xStackingOrder();
|
||||
|
|
|
@ -118,6 +118,13 @@ public:
|
|||
* Set's the Scene's Overlay X Window visibility to @p visible.
|
||||
**/
|
||||
void setOverlayWindowVisibility(bool visible);
|
||||
/**
|
||||
* @brief Hook for the Scene to notify about a that the last frame got rendered.
|
||||
*
|
||||
* In case the Compositor was waiting for the frame being rendered, the next rendering process
|
||||
* is triggered.
|
||||
*/
|
||||
void lastFrameRendered();
|
||||
|
||||
Scene *scene() {
|
||||
return m_scene;
|
||||
|
@ -296,6 +303,7 @@ private:
|
|||
bool m_starting; // start() sets this variable while starting
|
||||
qint64 m_timeSinceLastVBlank;
|
||||
Scene *m_scene;
|
||||
bool m_waitingForFrameRendered;
|
||||
|
||||
KWIN_SINGLETON_VARIABLE(Compositor, s_compositor)
|
||||
};
|
||||
|
|
|
@ -20,6 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#define WL_EGL_PLATFORM 1
|
||||
#include "egl_wayland_backend.h"
|
||||
// kwin
|
||||
#include "composite.h"
|
||||
#include "options.h"
|
||||
#include "wayland_backend.h"
|
||||
#include "xcbutils.h"
|
||||
|
@ -33,6 +34,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
namespace KWin
|
||||
{
|
||||
|
||||
static void handleFrameCallback(void *data, wl_callback *callback, uint32_t time)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(time)
|
||||
reinterpret_cast<EglWaylandBackend*>(data)->lastFrameRendered();
|
||||
|
||||
if (callback) {
|
||||
wl_callback_destroy(callback);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct wl_callback_listener s_surfaceFrameListener = {
|
||||
handleFrameCallback
|
||||
};
|
||||
|
||||
EglWaylandBackend::EglWaylandBackend()
|
||||
: QObject(NULL)
|
||||
, OpenGLBackend()
|
||||
|
@ -40,6 +56,7 @@ EglWaylandBackend::EglWaylandBackend()
|
|||
, m_bufferAge(0)
|
||||
, m_wayland(Wayland::WaylandBackend::self())
|
||||
, m_overlay(NULL)
|
||||
, m_lastFrameRendered(true)
|
||||
{
|
||||
if (!m_wayland) {
|
||||
setFailed("Wayland Backend has not been created");
|
||||
|
@ -236,6 +253,9 @@ void EglWaylandBackend::present()
|
|||
// need to dispatch pending events as eglSwapBuffers can block
|
||||
m_wayland->dispatchEvents();
|
||||
|
||||
m_lastFrameRendered = false;
|
||||
wl_callback *callback = wl_surface_frame(m_wayland->surface());
|
||||
wl_callback_add_listener(callback, &s_surfaceFrameListener, this);
|
||||
if (supportsBufferAge()) {
|
||||
eglSwapBuffers(m_display, m_surface);
|
||||
eglQuerySurface(m_display, m_surface, EGL_BUFFER_AGE_EXT, &m_bufferAge);
|
||||
|
@ -338,6 +358,17 @@ void EglWaylandBackend::overlaySizeChanged(const QSize &size)
|
|||
wl_egl_window_resize(m_overlay, size.width(), size.height(), 0, 0);
|
||||
}
|
||||
|
||||
bool EglWaylandBackend::isLastFrameRendered() const
|
||||
{
|
||||
return m_lastFrameRendered;
|
||||
}
|
||||
|
||||
void EglWaylandBackend::lastFrameRendered()
|
||||
{
|
||||
m_lastFrameRendered = true;
|
||||
Compositor::self()->lastFrameRendered();
|
||||
}
|
||||
|
||||
/************************************************
|
||||
* EglTexture
|
||||
************************************************/
|
||||
|
|
|
@ -66,7 +66,9 @@ public:
|
|||
virtual void endRenderingFrame(const QRegion &renderedRegion, const QRegion &damagedRegion);
|
||||
virtual bool makeCurrent() override;
|
||||
virtual void doneCurrent() override;
|
||||
virtual bool isLastFrameRendered() const override;
|
||||
Xcb::Shm *shm();
|
||||
void lastFrameRendered();
|
||||
|
||||
protected:
|
||||
virtual void present();
|
||||
|
@ -88,6 +90,7 @@ private:
|
|||
Wayland::WaylandBackend *m_wayland;
|
||||
wl_egl_window *m_overlay;
|
||||
QScopedPointer<Xcb::Shm> m_shm;
|
||||
bool m_lastFrameRendered;
|
||||
friend class EglWaylandTexture;
|
||||
};
|
||||
|
||||
|
|
9
scene.h
9
scene.h
|
@ -121,6 +121,15 @@ public:
|
|||
virtual bool makeOpenGLContextCurrent();
|
||||
virtual void doneOpenGLContextCurrent();
|
||||
|
||||
/**
|
||||
* @brief Allows the Compositor to delay the rendering of the next frame until the last one
|
||||
* has been rendered. This is mostly interesting in case that the system compositor is not able
|
||||
* to keep up with KWin's frame rate.
|
||||
*
|
||||
* @return bool @c true if the next frame should be rendered, @c false otherwise
|
||||
*/
|
||||
virtual bool isLastFrameRendered() const = 0;
|
||||
|
||||
public Q_SLOTS:
|
||||
// a window has been destroyed
|
||||
void windowDeleted(KWin::Deleted*);
|
||||
|
|
|
@ -133,6 +133,11 @@ QRegion OpenGLBackend::accumulatedDamageHistory(int bufferAge) const
|
|||
return region;
|
||||
}
|
||||
|
||||
bool OpenGLBackend::isLastFrameRendered() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/************************************************
|
||||
* SceneOpenGL
|
||||
***********************************************/
|
||||
|
|
|
@ -56,6 +56,7 @@ public:
|
|||
virtual bool syncsToVBlank() const;
|
||||
virtual bool makeOpenGLContextCurrent() override;
|
||||
virtual void doneOpenGLContextCurrent() override;
|
||||
virtual bool isLastFrameRendered() const override;
|
||||
|
||||
void idle();
|
||||
|
||||
|
@ -475,6 +476,13 @@ public:
|
|||
virtual void endRenderingFrame(const QRegion &damage, const QRegion &damagedRegion) = 0;
|
||||
virtual bool makeCurrent() = 0;
|
||||
virtual void doneCurrent() = 0;
|
||||
/**
|
||||
* @brief Backend specific code to determine whether the last frame got rendered.
|
||||
*
|
||||
* Default implementation always returns @c true. That is it's always assumed that the last
|
||||
* frame got rendered. If a backend needs more control it needs to implement this method.
|
||||
*/
|
||||
virtual bool isLastFrameRendered() const;
|
||||
/**
|
||||
* @brief Compositor is going into idle mode, flushes any pending paints.
|
||||
**/
|
||||
|
@ -670,6 +678,11 @@ inline bool SceneOpenGL::hasPendingFlush() const
|
|||
return m_backend->hasPendingFlush();
|
||||
}
|
||||
|
||||
inline bool SceneOpenGL::isLastFrameRendered() const
|
||||
{
|
||||
return m_backend->isLastFrameRendered();
|
||||
}
|
||||
|
||||
inline SceneOpenGL::Texture* OpenGLWindowPixmap::texture() const
|
||||
{
|
||||
return m_texture.data();
|
||||
|
|
|
@ -137,6 +137,11 @@ void XRenderBackend::screenGeometryChanged(const QSize &size)
|
|||
Q_UNUSED(size)
|
||||
}
|
||||
|
||||
bool XRenderBackend::isLastFrameRendered() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//****************************************
|
||||
// X11XRenderBackend
|
||||
//****************************************
|
||||
|
|
|
@ -67,6 +67,13 @@ public:
|
|||
* @param size The new screen size
|
||||
*/
|
||||
virtual void screenGeometryChanged(const QSize &size);
|
||||
/**
|
||||
* @brief Backend specific code to determine whether the last frame got rendered.
|
||||
*
|
||||
* Default implementation always returns @c true. That is it's always assumed that the last
|
||||
* frame got rendered. If a backend needs more control it needs to implement this method.
|
||||
*/
|
||||
virtual bool isLastFrameRendered() const;
|
||||
/**
|
||||
* @brief The compositing buffer hold by this backend.
|
||||
*
|
||||
|
@ -156,6 +163,9 @@ public:
|
|||
virtual OverlayWindow *overlayWindow() {
|
||||
return m_backend->overlayWindow();
|
||||
}
|
||||
virtual bool isLastFrameRendered() const {
|
||||
return m_backend->isLastFrameRendered();
|
||||
}
|
||||
|
||||
static SceneXrender *createScene();
|
||||
protected:
|
||||
|
|
Loading…
Reference in a new issue