Skip to content

renderer/image: add interruptible transition rendering with custom sh… - #105

Open
markg85 wants to merge 1 commit into
hyprwm:mainfrom
markg85:wallpaper_shader_transition
Open

renderer/image: add interruptible transition rendering with custom sh…#105
markg85 wants to merge 1 commit into
hyprwm:mainfrom
markg85:wallpaper_shader_transition

Conversation

@markg85

@markg85 markg85 commented Jul 7, 2026

Copy link
Copy Markdown

Companion to: hyprwm/hyprpaper#371

Note: This is written against tag v0.5.4. Consider this a draft!

@markg85
markg85 force-pushed the wallpaper_shader_transition branch from e8edd92 to 0004bf2 Compare July 19, 2026 22:12
@markg85
markg85 marked this pull request as ready for review July 19, 2026 22:20
Comment thread include/hyprtoolkit/element/Image.hpp Outdated
Comment thread include/hyprtoolkit/element/Image.hpp Outdated
…aders

Adds an animated wallpaper transition path to CImageElement: a two-texture
blend (start -> end) driven by fadeLayersIn/Out durations, with an optional
custom GLSL fragment shader supplied by path.

- CImageElement::transitionTo captures the in-flight blend to an FBO when a
  transition is interrupted mid-way, using that as the new start texture so a
  fresh transition can begin to the new target (no flicker, no black flash).
- IRenderer gains renderTransition / captureTransitionState / ensureTransitionShader.
  Custom shaders are stat()-checked once at transition start and recompiled only
  on mtime change; the per-frame render path is a single cache lookup + draw,
  with no filesystem or compile work.
- CFramebuffer sets m_tex->m_size after glTexImage2D so captured textures report
  the right size to the shader's tex1Size/tex2Size uniforms.
- A captured texture keeps its owning CFramebuffer alive via m_tempFramebuffer.
- All fit modes (stretch/cover/contain/tile) are honoured; the host pushes the
  config duration to u_duration each frame (GLES cannot initialize uniforms).
@markg85
markg85 force-pushed the wallpaper_shader_transition branch from 0004bf2 to a13cbc7 Compare July 22, 2026 15:32
@markg85

markg85 commented Jul 24, 2026

Copy link
Copy Markdown
Author

Please re-review. The double duration (intended to mimic the config) is resolved to just one time. And the shader path now is it's raw data instead.

I updated the code with force push to keep 1 commit and keep things clean. Let me know if you like that or if you rather have individual commits from now on. To me either way works so just let me know your preference here.

@vaxerski

Copy link
Copy Markdown
Member

@codex review please

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a13cbc762b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


// Keep the FBO alive for as long as the texture is. The captured blend is
// already fit-resolved, so mark it to prevent a second fit pass.
capturedTex->m_tempFramebuffer = captureFb;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Break the framebuffer-texture ownership cycle

Every captured transition texture permanently leaks its framebuffer and GL texture: CFramebuffer strongly owns m_tex, and this assignment makes that texture strongly own the same framebuffer. Dropping startTexture or clearing m_fitFrameCache therefore cannot invoke either destructor; renderFitFrame() creates the same cycle at its corresponding assignment, so repeated full-screen transitions continually accumulate GPU memory until allocation failure or OOM.

Useful? React with 👍 / 👎.

Comment on lines +306 to +308
m_impl->data.path = path;
m_impl->data.fitMode = fitMode;
renderTex();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Queue the newest image when interrupting an active load

When transitionTo() is called again before the previous target finishes loading, waitingForTex is still true, so this renderTex() call immediately returns without starting or deferring a load for the new path. The first request's cache entry later becomes the transition end texture, meaning an interrupted transition displays the superseded image and the newest path is never loaded.

Useful? React with 👍 / 👎.

Comment on lines +333 to +337
if (trans.progress >= 1.0f) {
trans.active = false;
trans.startTexture.reset();
trans.endTexture.reset();
g_renderer->clearTransitionCache();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep rendering the outgoing frame until the target is ready

If decoding or uploading the target takes longer than the requested duration, this completion branch disables the transition and discards its start texture even though cacheEntry->tex() is still unavailable. The current damaged frame consequently draws nothing and no further animation frame is scheduled, leaving the image transparent until the asynchronous load eventually completes; the available outgoing oldCacheEntry should remain visible in this case.

Useful? React with 👍 / 👎.

Comment on lines +972 to +975
GLuint prog = createProgram(loadShader("tex300.vert"), source);
if (!prog) {
g_logger->log(HT_LOG_ERROR, "ensureTransitionShader: failed to compile shader program");
return 0;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Handle invalid custom shaders without aborting

When caller-provided shaderSource contains a GLSL compilation or link error, createProgram() reaches an RASSERT in compileShader() or during linking and raises SIGABRT. Thus a malformed custom transition shader crashes the whole application, and the following if (!prog) recovery path can never run; compilation needs a nonfatal failure path that falls back to the default shader.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants