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

View file

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

View file

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

View file

@ -14,7 +14,7 @@
#include <QEasingCurve> #include <QEasingCurve>
#include <QElapsedTimer> #include <QElapsedTimer>
#include <QtMath> #include <QtMath>
#include <kwineffects.h> #include <kwindeformeffect.h>
#include <kwineffects_export.h> #include <kwineffects_export.h>
namespace KWin namespace KWin
@ -191,7 +191,7 @@ class AnimationEffectPrivate;
* *
* @since 4.8 * @since 4.8
*/ */
class KWINEFFECTS_EXPORT AnimationEffect : public Effect class KWINEFFECTS_EXPORT AnimationEffect : public DeformEffect
{ {
Q_OBJECT Q_OBJECT
@ -217,6 +217,16 @@ public:
Clip, Clip,
Generic, Generic,
CrossFadePrevious, 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 NonFloatBase = Position
}; };
Q_ENUM(Attribute) Q_ENUM(Attribute)
@ -360,12 +370,13 @@ protected:
* @param fullScreen Sets this effect as the active full screen effect for the * @param fullScreen Sets this effect as the active full screen effect for the
* duration of the animation. * duration of the animation.
* @param keepAlive Whether closed windows should be kept alive during 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. * @returns An ID that you can use to cancel a running animation.
* @since 4.8 * @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 * @param fullScreen Sets this effect as the active full screen effect for the
* duration of the animation. * duration of the animation.
* @param keepAlive Whether closed windows should be kept alive during 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. * @returns An ID that you need to use to cancel this manipulation.
* @since 4.11 * @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; AniMap state() const;
private: 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; QRect clipRect(const QRect &windowRect, const AniData &) const;
float interpolated(const AniData &, int i = 0) const; float interpolated(const AniData &, int i = 0) const;
float progress(const AniData &) 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_MAKE_VERSION(major, minor) ((major) << 8 | (minor))
#define KWIN_EFFECT_API_VERSION_MAJOR 0 #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( \ #define KWIN_EFFECT_API_VERSION KWIN_EFFECT_API_MAKE_VERSION( \
KWIN_EFFECT_API_VERSION_MAJOR, KWIN_EFFECT_API_VERSION_MINOR) KWIN_EFFECT_API_VERSION_MAJOR, KWIN_EFFECT_API_VERSION_MINOR)