[libkwineffects] Add support for shaders in AnimationEffect

The animate and set calls are extended for an optional GLShader* to
allow specifying a custom shader to use during the animation.

To properly support rendering a complete window in the effect the
AnimationEffect gets based on the DeformEffect. If a shader is used
during the animation the window gets redirected.

For the animation with shaders two new enum values are added to the
AnimationType enum:
 * Shader
 * ShaderUniform

The Shader animation type is for specifying that the animation uses a
shader. During the animation a uniform "animationProgress" is set on the
shader.

The ShaderUniform animation type behaves exactly like the Shader type,
but also animates a user provided uniform. The meta data of the
animation is interpreted as a uniform location for a float uniform and
during the animation this uniform is updated with the interpolated
animation data.
This commit is contained in:
Martin Flöser 2022-04-11 20:45:18 +02:00
parent 7dddf3055b
commit 47b330ea23
5 changed files with 55 additions and 12 deletions

View file

@ -72,7 +72,7 @@ AniData::AniData()
AniData::AniData(AnimationEffect::Attribute a, int meta_, const FPx2 &to_,
int delay, const FPx2 &from_, bool waitAtSource_,
FullScreenEffectLockPtr fullScreenEffectLock_, bool keepAlive,
PreviousWindowPixmapLockPtr previousWindowPixmapLock_)
PreviousWindowPixmapLockPtr previousWindowPixmapLock_, GLShader *shader)
: attribute(a)
, from(from_)
, to(to_)
@ -84,6 +84,7 @@ AniData::AniData(AnimationEffect::Attribute a, int meta_, const FPx2 &to_,
, keepAlive(keepAlive)
, previousWindowPixmapLock(std::move(previousWindowPixmapLock_))
, lastPresentTime(std::chrono::milliseconds::zero())
, shader(shader)
{
}

View file

@ -69,7 +69,7 @@ public:
AniData(AnimationEffect::Attribute a, int meta, const FPx2 &to,
int delay, const FPx2 &from, bool waitAtSource,
FullScreenEffectLockPtr = FullScreenEffectLockPtr(),
bool keepAlive = true, PreviousWindowPixmapLockPtr previousWindowPixmapLock = {});
bool keepAlive = true, PreviousWindowPixmapLockPtr previousWindowPixmapLock = {}, GLShader *shader = nullptr);
bool isActive() const;
@ -94,6 +94,7 @@ public:
PreviousWindowPixmapLockPtr previousWindowPixmapLock;
AnimationEffect::TerminationFlags terminationFlags;
std::chrono::milliseconds lastPresentTime;
GLShader *shader{nullptr};
};
} // namespace

View file

@ -9,6 +9,7 @@
*/
#include "kwinanimationeffect.h"
#include "kwinglutils.h"
#include "anidata_p.h"
#include <QAction>
@ -207,7 +208,7 @@ void AnimationEffect::validate(Attribute a, uint &meta, FPx2 *from, FPx2 *to, co
}
}
quint64 AnimationEffect::p_animate(EffectWindow *w, Attribute a, uint meta, int ms, FPx2 to, const QEasingCurve &curve, int delay, FPx2 from, bool keepAtTarget, bool fullScreenEffect, bool keepAlive)
quint64 AnimationEffect::p_animate(EffectWindow *w, Attribute a, uint meta, int ms, FPx2 to, const QEasingCurve &curve, int delay, FPx2 from, bool keepAtTarget, bool fullScreenEffect, bool keepAlive, GLShader *shader)
{
const bool waitAtSource = from.isValid();
validate(a, meta, &from, &to, w);
@ -249,7 +250,8 @@ quint64 AnimationEffect::p_animate(EffectWindow *w, Attribute a, uint meta, int
waitAtSource, // Whether the animation should be kept at source
fullscreen, // Full screen effect lock
keepAlive, // Keep alive flag
previousPixmap // Previous window pixmap lock
previousPixmap, // Previous window pixmap lock
shader
));
const quint64 ret_id = ++d->m_animCounter;
@ -280,6 +282,9 @@ quint64 AnimationEffect::p_animate(EffectWindow *w, Attribute a, uint meta, int
} else {
triggerRepaint();
}
if (shader) {
DeformEffect::redirect(w);
}
return ret_id;
}
@ -405,6 +410,9 @@ bool AnimationEffect::cancel(quint64 animationId)
for (AniMap::iterator entry = d->m_animations.begin(), mapEnd = d->m_animations.end(); entry != mapEnd; ++entry) {
for (QList<AniData>::iterator anim = entry->first.begin(), animEnd = entry->first.end(); anim != animEnd; ++anim) {
if (anim->id == animationId) {
if (anim->shader && std::none_of(entry->first.begin(), entry->first.end(), [animationId] (const auto &anim) { return anim.id != animationId && anim.shader; })) {
unredirect(entry.key());
}
entry->first.erase(anim); // remove the animation
if (entry->first.isEmpty()) { // no other animations on the window, release it.
d->m_animations.erase(entry);
@ -642,11 +650,27 @@ void AnimationEffect::paintWindow(EffectWindow *w, int mask, QRegion region, Win
case CrossFadePrevious:
data.setCrossFadeProgress(progress(*anim));
break;
case Shader:
if (anim->shader && anim->shader->isValid()) {
ShaderBinder binder{anim->shader};
anim->shader->setUniform("animationProgress", progress(*anim));
setShader(w, anim->shader);
}
break;
case ShaderUniform:
if (anim->shader && anim->shader->isValid()) {
ShaderBinder binder{anim->shader};
anim->shader->setUniform("animationProgress", progress(*anim));
anim->shader->setUniform(anim->meta, interpolated(*anim));
setShader(w, anim->shader);
}
break;
default:
break;
}
}
}
effects->paintWindow(w, mask, region, data);
}
@ -667,6 +691,9 @@ void AnimationEffect::postPaintScreen()
}
EffectWindow *window = entry.key();
d->m_justEndedAnimation = anim->id;
if (anim->shader && std::none_of(entry->first.begin(), entry->first.end(), [anim] (const auto &other) { return anim->id != other.id && other.shader; })) {
unredirect(window);
}
animationEnded(window, anim->attribute, anim->meta);
d->m_justEndedAnimation = 0;
// NOTICE animationEnded is an external call and might have called "::animate"
@ -854,6 +881,8 @@ void AnimationEffect::updateLayerRepaints()
case Brightness:
case Saturation:
case CrossFadePrevious:
case Shader:
case ShaderUniform:
createRegion = true;
break;
case Rotation:

View file

@ -14,7 +14,7 @@
#include <QEasingCurve>
#include <QElapsedTimer>
#include <QtMath>
#include <kwineffects.h>
#include <kwindeformeffect.h>
#include <kwineffects_export.h>
namespace KWin
@ -191,7 +191,7 @@ class AnimationEffectPrivate;
*
* @since 4.8
*/
class KWINEFFECTS_EXPORT AnimationEffect : public Effect
class KWINEFFECTS_EXPORT AnimationEffect : public DeformEffect
{
Q_OBJECT
@ -217,6 +217,16 @@ public:
Clip,
Generic,
CrossFadePrevious,
/**
* Performs an animation with a provided shader.
* The float uniform @c animationProgress is set to the current progress of the animation.
**/
Shader,
/**
* Like Shader, but additionally allows to animate a float uniform passed to the shader.
* The uniform location must be provided as metadata.
**/
ShaderUniform,
NonFloatBase = Position
};
Q_ENUM(Attribute)
@ -360,12 +370,13 @@ protected:
* @param fullScreen Sets this effect as the active full screen effect for the
* duration of the animation.
* @param keepAlive Whether closed windows should be kept alive during animation.
* @param shader Optional shader to use to render the window.
* @returns An ID that you can use to cancel a running animation.
* @since 4.8
*/
quint64 animate(EffectWindow *w, Attribute a, uint meta, int ms, const FPx2 &to, const QEasingCurve &curve = QEasingCurve(), int delay = 0, const FPx2 &from = FPx2(), bool fullScreen = false, bool keepAlive = true)
quint64 animate(EffectWindow *w, Attribute a, uint meta, int ms, const FPx2 &to, const QEasingCurve &curve = QEasingCurve(), int delay = 0, const FPx2 &from = FPx2(), bool fullScreen = false, bool keepAlive = true, GLShader *shader = nullptr)
{
return p_animate(w, a, meta, ms, to, curve, delay, from, false, fullScreen, keepAlive);
return p_animate(w, a, meta, ms, to, curve, delay, from, false, fullScreen, keepAlive, shader);
}
/**
@ -392,12 +403,13 @@ protected:
* @param fullScreen Sets this effect as the active full screen effect for the
* duration of the animation.
* @param keepAlive Whether closed windows should be kept alive during animation.
* @param shader Optional shader to use to render the window.
* @returns An ID that you need to use to cancel this manipulation.
* @since 4.11
*/
quint64 set(EffectWindow *w, Attribute a, uint meta, int ms, const FPx2 &to, const QEasingCurve &curve = QEasingCurve(), int delay = 0, const FPx2 &from = FPx2(), bool fullScreen = false, bool keepAlive = true)
quint64 set(EffectWindow *w, Attribute a, uint meta, int ms, const FPx2 &to, const QEasingCurve &curve = QEasingCurve(), int delay = 0, const FPx2 &from = FPx2(), bool fullScreen = false, bool keepAlive = true, GLShader *shader = nullptr)
{
return p_animate(w, a, meta, ms, to, curve, delay, from, true, fullScreen, keepAlive);
return p_animate(w, a, meta, ms, to, curve, delay, from, true, fullScreen, keepAlive, shader);
}
/**
@ -506,7 +518,7 @@ protected:
AniMap state() const;
private:
quint64 p_animate(EffectWindow *w, Attribute a, uint meta, int ms, FPx2 to, const QEasingCurve &curve, int delay, FPx2 from, bool keepAtTarget, bool fullScreenEffect, bool keepAlive);
quint64 p_animate(EffectWindow *w, Attribute a, uint meta, int ms, FPx2 to, const QEasingCurve &curve, int delay, FPx2 from, bool keepAtTarget, bool fullScreenEffect, bool keepAlive, GLShader *shader);
QRect clipRect(const QRect &windowRect, const AniData &) const;
float interpolated(const AniData &, int i = 0) const;
float progress(const AniData &) const;

View file

@ -180,7 +180,7 @@ X-KDE-Library=kwin4_effect_cooleffect
#define KWIN_EFFECT_API_MAKE_VERSION(major, minor) ((major) << 8 | (minor))
#define KWIN_EFFECT_API_VERSION_MAJOR 0
#define KWIN_EFFECT_API_VERSION_MINOR 233
#define KWIN_EFFECT_API_VERSION_MINOR 234
#define KWIN_EFFECT_API_VERSION KWIN_EFFECT_API_MAKE_VERSION( \
KWIN_EFFECT_API_VERSION_MAJOR, KWIN_EFFECT_API_VERSION_MINOR)