feat: add wallpaper transitions, store crop in database per-wallpaper, move CUDA context creation to async thread

This commit is contained in:
2026-10-29 17:44:19 +01:00
parent 906b15b092
commit 3792e9d73f
26 changed files with 2338 additions and 210 deletions
+123 -8
View File
@@ -8,6 +8,7 @@
#include <vector>
#include <QImage>
#include <QHash>
#include <QList>
#include <QElapsedTimer>
#include <QRectF>
@@ -15,6 +16,7 @@
#include <QString>
#include <QStringList>
#include <QVariantMap>
#include <QVector>
#include <QtQml/qqml.h>
#include <qtmetamacros.h>
#include <atomic>
@@ -28,6 +30,7 @@
#include <wayland-client-protocol.h>
#include "wallpapersurface.hpp"
#include "config/enums.hpp"
struct mpv_handle;
struct mpv_render_context;
@@ -131,8 +134,76 @@ class WallpaperService : public QObject {
void storeGlobal(const char* interface, void* proxy);
struct TransProgram {
uint32_t id = 0;
int size = -1;
int progress = -1;
int from = -1;
int to = -1;
int fromCrop = -1;
int toCrop = -1;
int fromFlip = -1;
int toFlip = -1;
};
[[nodiscard]] bool setupGl();
[[nodiscard]] uint32_t createProgram();
[[nodiscard]] TransProgram createTransitionProgram(const QString& file);
[[nodiscard]] TransProgram programFor(const QString& file);
bool ensureTransitionProgram();
QString transitionFileFor(ZShell::config::WallpaperTransition::Enum value);
[[nodiscard]] QString randomTransitionFile();
void ensureCapture(const QSize& size);
uint32_t renderVideoIntoTexture(const QSize& size);
void drawSurfaceTransition(WallpaperSurface* surface);
struct TransitionLayer {
uint32_t tex = 0; // frozen image target (older layers)
QImage image; // CPU copy for crops/sizing (image layers)
QVariantMap crops; // crop map captured when the layer was pushed
QString transitionFile; // this layer's own transition shader
bool isVideo = false; // only the newest layer may be video
bool ready = false; // target is sampleable
bool started = false; // its own clock has begun
QElapsedTimer timer;
qreal progress = 0.0;
};
void ensureBaseFbo();
void ensureAccum(const QSize& size);
void bindRtt(uint32_t fbo, uint32_t tex, const QSize& size);
void drawTransitionPair(
const TransProgram& program,
uint32_t fromTex,
float fromFlip,
const QRectF& fromCrop,
uint32_t toTex,
float toFlip,
const QRectF& toCrop,
qreal progress,
const QSize& size);
uint32_t targetTexFor(const TransitionLayer& layer, int index) const;
QRectF cropForLayer(
const TransitionLayer& layer,
const QString& screen,
const QSize& size) const;
void captureBase(
const QSize& size,
const QString& screen,
uint32_t oldTex,
float oldFlip,
const QRectF& oldCrop);
void renderComposite(
const QSize& size,
const QString& screen,
uint32_t outFbo,
uint32_t outTex,
int first,
int last,
uint32_t videoTex,
uint32_t posterTex);
void uploadTexture(uint32_t& texture, const QImage& image);
void uploadImage();
@@ -150,8 +221,14 @@ class WallpaperService : public QObject {
[[nodiscard]] QByteArray cachedBlob(const QString& path) const;
[[nodiscard]] QByteArray posterBlob(const QString& path) const;
[[nodiscard]] QVariantMap cropsForPath(const QString& path) const;
void ensureImage();
void ensurePoster();
void notifyBufferSizes();
void queue(WallpaperSurface* surface);
@@ -187,7 +264,6 @@ class WallpaperService : public QObject {
bool m_isVideo = false;
bool m_videoReload = false;
bool m_previousVideo = false;
mpv_handle* m_mpv = nullptr;
mpv_render_context* m_mpvRender = nullptr;
uint32_t m_blackTex = 0;
@@ -223,6 +299,45 @@ class WallpaperService : public QObject {
int m_alphaUniform = -1;
int m_sourceUniform = -1;
bool m_randomTransition = false; // wallTransition == Random
QString m_transitionFile; // the transition stamped onto new layers
bool m_transitionDirty = false;
QHash<QString, TransProgram>
m_transPrograms; // compiled programs, keyed by shader
TransProgram
m_neutralProgram; // fade program for identity copies (seed/base/final)
QVector<int>
m_transitionUses; // usage counts, parallel to the transition list
QString m_lastTransition; // last random pick, so it is never repeated
uint32_t m_captureFbo = 0;
uint32_t m_captureTex = 0;
QSize m_captureSize;
uint32_t m_baseFbo = 0;
uint32_t m_baseTex = 0;
QSize m_baseSize;
bool m_baseReady = false;
uint32_t m_accumFbo = 0;
uint32_t m_accumA = 0;
uint32_t m_accumB = 0;
QSize m_accumSize;
std::vector<TransitionLayer> m_transitions; // oldest .. newest
bool m_needCaptureBase = false;
QImage m_baseImage; // old settled image, for the base capture crop
QVariantMap m_baseCrops; // old settled crop map, for the base capture crop
bool m_baseIsVideo = false; // old settled frame was a live video
bool m_baseIsBlack = false; // shell start / first load: fade in from black
bool m_collapseVideo =
false; // video pushed mid-stack: bake older into base
bool m_promoteOldest = false; // stack over the cap: promote oldest to base
bool m_newestDone = false; // newest layer finished: settle into base
static constexpr int kMaxTransitions = 4;
uint32_t m_videoProgram = 0;
uint32_t m_vao = 0;
int m_videoSizeUniform = -1;
@@ -240,14 +355,7 @@ class WallpaperService : public QObject {
bool m_usePlayer = true;
#endif
QImage m_previousImage;
QVariantMap m_previousCrops;
uint32_t m_previousTexture = 0;
std::vector<uint32_t> m_orphanTextures;
bool m_fading = false;
bool m_fadeClockStarted = false;
bool m_firstFadeDone = false;
QElapsedTimer m_fadeTimer;
QList<WallpaperSurface*> m_surfaces;
@@ -264,6 +372,13 @@ class WallpaperService : public QObject {
bool m_forceDecode = false;
bool m_imageChanged = false;
QImage m_posterImage;
uint32_t m_posterTex = 0;
QString m_posterPath;
bool m_posterReady = false;
bool m_posterLoading = false;
bool m_posterChanged = false;
};
} // namespace ZShell::internal