Skip to content

Optimizing Background Video/Image Renderer - #915

Merged
neon-nyan merged 67 commits into
mainfrom
feat/mt-bg-renderer
Oct 21, 2026
Merged

neon-nyan merged 67 commits into
mainfrom
feat/mt-bg-renderer

Conversation

@neon-nyan

@neon-nyan neon-nyan commented Oct 21, 2026 •

Copy link
Copy Markdown
Member

Main Goal

Optimizing the Background Video/Image Load routines and provides better memory allocation after dispose/use of each frames. The following PR also provides major improvements on the frame renderer for the background video playback, including direct GPU drawn to the frame's IDirect3DTexture outside of the UI thread, which reduces the VRAM usage and (hopefully) stutters.

Reduced Memory and VRAM Allocation

Video Background Playing (Honkai Impact 3rd - Mainland China - Picture 2 1080p)

Mem: Current Release Mem: This PR VRAM: Current Release VRAM: This PR

Video Background On-Pause/Window minimized (Custom Video 2160p)

Mem: Current Release Mem: This PR VRAM: Current Release VRAM: This PR

Other Improvements

[Fix] Missing Alpha Channel on HEIF/AVIF Encoded Background Image.

The Alpha channel went missing while decoding HEIF/AVIF Encoded Images which caused by incorrect decoding request via the PhotoSauce.MagicScaler library. A bug fix has been submitted into a PR here and while waiting for the PR to be merged, we copied our version of the workaround here.

Source (AVIF encoded) Decoded (.png) Before Decoded (.png) After

[Imp] Reencoding Sprites to AVIF

Previously, all the static sprites within Collapse assets (including Logo and Default Posters) are encoded using lossless PNG format with reduced palettes (256 colors + dithering). However, the size of the sprites are still considerably huge. So now, we decided to re-encode most of the sprites to AVIF with high bit-depth (up to 16-bit) while maintaining a smaller overall file sizes. The size reduction might be not that noticeable.

Before (Current Release) After (This PR)

PR Status :

  • Overall Status : Done
  • Commits : Done
  • Synced to base (Collapse:main) : Yes
  • Build status : OK
  • Crashing : No
  • Bug found caused by PR : Unknown

Templates

Changelog Prefixes
  **[New]**
  **[Imp]**
  **[Fix]**
  **[Loc]**
  **[Doc]**

Parameters:
Quality: 70
Subsample chroma: 4:2:0
Sharp YUV Downsampling: Enabled
Sharpness: 7
Noise synthesis: 30
Effort: 7
Previously, the Window will only be decided as "Hidden" if no WS_MINIMIZE is present. This causes an issue where a window with minimized state but hidden into a tray will return the condition as true.

Adding barrier to check if no WS_VISIBLE was present, then assume it as hidden as well.
@socket-security

socket-security Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedmicrosoft.dotnet.ilcompiler@​10.0.1310010010010070
Addedruntime.win-x64.microsoft.dotnet.ilcompiler@​10.0.13971009010070

View full report

Comment on lines 270 to +280
.ConfigureAwait(false);

if (color == default) return;

string hex = $"{color.R:X2}{color.G:X2}{color.B:X2}";
if (!string.IsNullOrEmpty(configKey))
{
string? savedHex = LauncherConfig.GetAppConfigValue($"{configKey}-AccentColor").ToString();
if (savedHex == hex) return;
LauncherConfig.SetAndSaveConfigValue($"{configKey}-AccentColor", hex);
}

ColorAccentChanged?.Invoke(color);
}
catch (Exception ex)
{
Logger.LogWriteLine($"GetMediaAccentColor failed: {ex}", LogType.Error, true);
Logger.LogWriteLine($"LoadMediaAccentColor failed: {ex}", LogType.Error, true);
SentryHelper.ExceptionHandler(ex);
}
}
}
} No newline at end of file

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: 10-bit H.264 video backgrounds fail to decode when the FFmpeg toggle is disabled because Media Foundation does not support High 10 profile decoding.
Severity: MEDIUM

Suggested Fix

Restore the check for 10-bit video pixel formats (such as YUV420P10) in video codec detection to force FFmpeg decoding even if the global FFmpeg toggle is disabled, or provide a fallback when Media Foundation fails to decode.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location:
CollapseLauncher/Classes/GameManagement/ImageBackground/ImageBackgroundManager.Loaders.cs#L26-L280

Potential issue: Removing the `RequiresFFmpegDecoder` check for YUV 10-bit formats
(`AV_PIX_FMT_YUV420P10LE`/`BE`) causes 10-bit H.264 background videos to bypass FFmpeg
when the 'use FFmpeg' toggle is disabled. In this configuration,
`CheckCodecOrSpawnDialog` returns `(true, false)`, routing playback through Windows
Media Foundation. Because Windows' H.264 decoder MFT only supports up to 8-bit profiles
(Baseline/Main/High), playback fails without an FFmpeg fallback or `MediaFailed`
handler, leaving the background blank.

Did we get this right? 👍 / 👎 to inform future reviews.

Comment on lines +240 to +250

private static ReadOnlySpan<char> TryGetFileExtensionFromUri(Uri uri)
{
ReadOnlySpan<char> pathAndQuery = uri.PathAndQuery;
int indexOfQuerySign = pathAndQuery.IndexOf('?');
if (indexOfQuerySign > 0)
pathAndQuery = pathAndQuery[..indexOfQuerySign];

return Path.GetExtension(pathAndQuery);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: ReadBitmapImageFromUriDetached requests unnecessary write access via FileAccess.ReadWrite, causing load failures on read-only or exclusively locked image files.
Severity: LOW

Suggested Fix

Change FileAccess.ReadWrite to FileAccess.Read and adjust FileShare to FileShare.Read in ReadBitmapImageFromUriDetached when opening source image streams.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location:
CollapseLauncher/XAMLs/Theme/CustomControls/LayeredBackgroundImageExtensions.ImageSourceFactory.cs#L1-L250

Potential issue: `ReadBitmapImageFromUriDetached` opens images using `File.Open(...,
FileAccess.ReadWrite, FileShare.ReadWrite)` even though the operation is read-only. When
referencing a native-format image directly (such as an uncropped custom background below
the minimum crop dimension), opening a read-only file or a file held open without write
sharing throws an `UnauthorizedAccessException` or sharing violation. Because the logger
passed from `PresenterFactory` is null, the exception is swallowed silently and the
background remains blank.

Did we get this right? 👍 / 👎 to inform future reviews.

Comment on lines +89 to +98
if (source is Uri sourceAsUri ||
(source is string sourceUriAsString &&
Uri.TryCreate(sourceUriAsString, UriKind.Absolute, out sourceAsUri!)))
{
if (sourceAsUri.IsFile)
{
string fullyQualifiedPath = sourceAsUri.LocalPath.GetFullyQualifiedPath();
ffmpegMediaSource = await FFmpegMediaSource
.CreateFromFileAsync(fullyQualifiedPath,
ffmpegConfig,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: Local path string sources construct redundant FFmpegMediaSource instances in GetMediaSourceAsync, leaking the first instance and its open file handle.
Severity: MEDIUM

Suggested Fix

Ensure GetMediaSourceAsync only creates a single FFmpegMediaSource for string/Uri inputs, structuring conditional branches to avoid repeating CreateFromFileAsync or disposing existing instances before reassigning.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location:
CollapseLauncher/XAMLs/Theme/CustomControls/LayeredBackgroundImageExtensions.MediaSourceFactory.cs#L89-L98

Potential issue: In `GetMediaSourceAsync` when `useFFmpeg` is enabled and the source is
passed as a local-path string, both the initial `Uri` check and the subsequent string
check construct an `FFmpegMediaSource` instance via `CreateFromFileAsync`. The first
created instance is overwritten by the second without being disposed, leaking its
unmanaged resources and underlying file handle until GC/RCW finalization.

Comment on lines +79 to +89
if (sender is not Image asImage)
{
return;
}

asImage.ImageOpened -= ImagePresenterOnLoaded;
asImage.ImageFailed -= ImagePresenterOnLoaded;
asImage.Loaded -= ImagePresenterOnLoaded;
await UnloadLastPresenter(parent, asImage);
tcs.TrySetResult();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: AddImagePresenterAsync is called without an ImageFailed handler, causing LoadPresenter to hang indefinitely if image decoding fails.
Severity: MEDIUM

Suggested Fix

Pass an ImageFailed event handler to AddImagePresenterAsync that transitions the TaskCompletionSource to an exception or completed state when image loading fails.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location:
CollapseLauncher/XAMLs/Theme/CustomControls/LayeredBackgroundImage.Loader.cs#L71-L89

Potential issue: When adding an image background presenter in
`LayeredBackgroundImage.Loader.cs`, `AddImagePresenterAsync` is invoked without
providing an `onFailedEventHandler` (passing `null`). If image decoding fails, neither
`ImageOpened` nor any failure handler triggers completion on the underlying
`TaskCompletionSource`, causing `LoadPresenter` to await `tcs.Task` indefinitely and
leaving previous background presenters loaded.

For now, just rely on GC to automatically free them, just call Dispose if an object is an IDisposable
Comment on lines +52 to +62
// -- Get context and invalidate previous CTS
LayeredImageBackgroundContext? context = CurrentSelectedBackgroundContext;
if (context == null)
return;

private void CancelCurrentBackgroundLoad()
{
if (Interlocked.Exchange(ref _imageLoadingTokenSource, null) is { } lastCts)
{
lastCts.Cancel();
lastCts.Dispose();
}
IsBackgroundLoading = false;
}

public void PreloadBackground(PresetConfig? preset, Grid? presenterGrid)
{
if (preset == null || presenterGrid == null) return;

CancelCurrentBackgroundLoad();
Interlocked.Increment(ref _loadGeneration);

PresenterGrid = presenterGrid;
string autoPlayKey = $"LastIsEnableBackgroundAutoPlay-{preset.GameName}-{preset.ZoneName}";
CurrentIsEnableBackgroundAutoPlayKey = autoPlayKey;

string cachedBgKey = $"CachedBg-{preset.GameName}-{preset.ZoneName}";
string? backgroundUrl = LauncherConfig.GetAppConfigValue($"{cachedBgKey}-Background").ToString();
string? staticBgUrl = LauncherConfig.GetAppConfigValue($"{cachedBgKey}-StaticBackground").ToString();
string? overlayUrl = LauncherConfig.GetAppConfigValue($"{cachedBgKey}-Overlay").ToString();

if (string.IsNullOrEmpty(backgroundUrl) && string.IsNullOrEmpty(staticBgUrl))
{
IsBackgroundLoading = true;
return;
}
// -- Invalidate last Cancellation Token (and thus, cancel all asynchronous routines)
// and then renew the token. Also toggle the background loading indicator.
DispatcherQueueExtensions.CurrentDispatcherQueue.TryEnqueue(() => IsBackgroundLoading = true );
token = InvalidateAndRenewCancelToken(token);

string? primaryBg = backgroundUrl ?? staticBgUrl;
stopwatch = Stopwatch.StartNew();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: Cancelled background loading tasks unconditionally set IsBackgroundLoading = false in finally, prematurely hiding the loading indicator while a newer request is still in progress.
Severity: MEDIUM

Suggested Fix

In the finally block of ImageBackgroundManager.Loaders.cs, guard the dispatch that sets IsBackgroundLoading = false with a check ensuring that cancellation has not been requested for the task's token.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location:
CollapseLauncher/Classes/GameManagement/ImageBackground/ImageBackgroundManager.Loaders.cs#L47-L62

Potential issue: In `ImageBackgroundManager.Loaders.cs`, when a background load
operation is cancelled by a newer request (e.g., rapidly changing background indexes),
the cancelled task's `finally` block unconditionally enqueues `IsBackgroundLoading =
false`. Because the check `if (!token.IsCancellationRequested)` was removed, the
cancellation of an earlier task prematurely resets `IsBackgroundLoading` to `false` on
the UI thread while the newer load task is still downloading. This dismisses the loading
indicator and re-enables UI controls prematurely.

Comment on lines +576 to +582
Task<(string, ImageExternalCodecType)> processTask = ImplAsync();
if (processTask.IsCompleted)
{
return (filePath, codecType);
return new ValueTask<(string, ImageExternalCodecType)>(processTask.Result);
}

// Return null if the codec type is not supported.
if (codecType == ImageExternalCodecType.NotSupported)
return new ValueTask<(string, ImageExternalCodecType)>(processTask);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: Failed background image loads no longer invoke OnBackgroundLoadFailed, preventing fallback placeholder images from displaying.
Severity: MEDIUM

Suggested Fix

Restore the invocation of OnBackgroundLoadFailed?.Invoke() when the resolved background URI is null or invalid before returning.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location:
CollapseLauncher/Classes/GameManagement/ImageBackground/ImageBackgroundManager.ImageCropperAndConvert.cs#L576-L582

Potential issue: The invocation of `OnBackgroundLoadFailed` was removed during
refactoring. When background, static, or overlay URLs are missing or fail to resolve,
the method now silently returns after setting `backgroundToUse = null` instead of firing
the failure callback. Consequently, `PlaceholderBackgroundLayer` is never inserted by
`MainPage`, leaving `OnBackgroundLoadFailed` unused and displaying an outdated
background rather than the fallback preset placeholder.

@neon-nyan
neon-nyan merged commit c040bb6 into main Oct 21, 2026
9 of 10 checks passed
@neon-nyan neon-nyan mentioned this pull request Oct 21, 2026
neon-nyan added a commit that referenced this pull request Oct 21, 2026
![UpdateIsAvailable-Campaign-Columbina](https://github.com/user-attachments/assets/e7043018-4d49-48f2-adf1-3c846fd3c3e4)
# Preview 1.84.7 (Codename: Columbina)

# What's Changed? - 1.84.7
## **[New]** [Game Settings Subsystem Support for Supported Plugin-based
Games](#900), by
@Cryotechnic
We have implemented a new Update 6 extension for v1 API Standard. The
plugin developer could now add customizations to the game into Collapse
Launcher. To see the example of the implementation, see the source code
of our Wuthering Waves plugin
[here](https://github.com/CollapseLauncher/Hi3Helper.Plugin.Wuwa/tree/main/Hi3Helper.Plugin.Wuwa/Management/Settings).

<img width="962" height="541" alt="image"
src="https://github.com/user-attachments/assets/1f668111-fbf7-445a-b90c-70c1693ba87b"
/>

## **[Imp]** [Optimizing Background Video/Image
Renderer](#915), by
@neon-nyan
Optimizing the Background Video/Image Load routines and provides better
memory allocation after dispose/use of each frames. The following PR
also provides major improvements on the frame renderer for the
background video playback, including direct GPU drawn to the frame's
IDirect3DTexture outside of the UI thread, which reduces the VRAM usage
and (hopefully) stutters.

### Video Background Playing (Honkai Impact 3rd - Mainland China -
Picture 2 1080p)
| Mem: Current Release | Mem: This PR | VRAM: Current Release | VRAM:
This PR |
|----|----|----|----|
|
![](https://github.com/user-attachments/assets/571431d9-aebc-438b-b2de-6386eb91654f)
|
![](https://github.com/user-attachments/assets/81a8c619-c29c-44cf-b35a-c17b92399c4a)
|
![](https://github.com/user-attachments/assets/29a3d4b7-fc4d-4e2c-9795-793d089efde9)
|
![](https://github.com/user-attachments/assets/455d75ae-bba1-4b73-aa0b-350114b6227b)
|
### Video Background On-Pause/Window minimized (Custom Video 2160p)
| Mem: Current Release | Mem: This PR | VRAM: Current Release | VRAM:
This PR |
|----|----|----|----|
|
![](https://github.com/user-attachments/assets/8894a3f4-f67c-4d17-a008-189ad2299a75)
|
![](https://github.com/user-attachments/assets/4da743c4-ba44-4859-bd8f-b080bd61038b)
|
![](https://github.com/user-attachments/assets/1f1dcb14-5998-4ec3-b967-bd4cc2a1bd0f)
|
![](https://github.com/user-attachments/assets/a5101117-eb56-41c5-84da-ca28f1d02fcf)
|

- **[Fix]** Race Conditions in Sophon API Loader causing Available Game
Version reverted to Deprecated ZIP API, by @neon-nyan
- **[New]** Sync game settings to database by @bagusnl, @neon-nyan 
- Now users with DB subsystem set-up can backup their game settings to
the configured DB endpoint. Uses Collapse' way of saving registry so
storage shouldn't be a problem.
- Guide on setting Collapse' DB subsystem is here
https://collapselauncher.com/feature/database.html
- **[Imp]** DiscordRPC fixes and stability improvements by @neon-nyan 
- **[Imp]** Improved Collapse settings loading time by initializing
Waifu2x at startup time by @neon-nyan
- **[Imp]** Improved video background playback by reducing animation in
carousel by @neon-nyan
  - Should be smoother and less prone of frame skip now
- **[Imp]** Background CPU and Memory utilizations by @neon-nyan 
- CPU utilization should be next to 0 when Collapse is minimized (and to
tray) by hiding WinUI elements.
- Memory utilization is improved by running more aggressive
garbage-collections
- **[Fix]** Region without static images uses last loaded region
background resources by @neon-nyan


<details>
  <summary>Changelog Prefixes</summary>
  
  ```
    **[New]**
    **[Imp]**
    **[Fix]**
    **[Loc]**
    **[Doc]**
  ```

</details>
@neon-nyan neon-nyan mentioned this pull request Oct 21, 2026
neon-nyan added a commit that referenced this pull request Oct 21, 2026
![UpdateIsAvailable-Campaign-Columbina](https://github.com/user-attachments/assets/e7043018-4d49-48f2-adf1-3c846fd3c3e4)
# Stable 1.84.7 (Codename: Columbina)

# What's Changed? - 1.84.7
## **[New]** [Game Settings Subsystem Support for Plugin-based
Games](#900), by
@Cryotechnic
We have implemented a new Update 6 extension for v1 API Standard. The
plugin developer could now add customizations to the game into Collapse
Launcher. To see the example of the implementation, see the source code
of our Wuthering Waves plugin
[here](https://github.com/CollapseLauncher/Hi3Helper.Plugin.Wuwa/tree/main/Hi3Helper.Plugin.Wuwa/Management/Settings).

<img width="962" height="541" alt="image"
src="https://github.com/user-attachments/assets/1f668111-fbf7-445a-b90c-70c1693ba87b"
/>

## **[Imp]** [Optimizing Background Video/Image
Renderer](#915), by
@neon-nyan
Optimizing the Background Video/Image Load routines and provides better
memory allocation after dispose/use of each frames. The following PR
also provides major improvements on the frame renderer for the
background video playback, including direct GPU drawn to the frame's
IDirect3DTexture outside of the UI thread, which reduces the VRAM usage
and (hopefully) stutters.

### Video Background Playing (Honkai Impact 3rd - Mainland China -
Picture 2 1080p)
| Mem: Current Release | Mem: This PR | VRAM: Current Release | VRAM:
This PR |
|----|----|----|----|
|
![](https://github.com/user-attachments/assets/571431d9-aebc-438b-b2de-6386eb91654f)
|
![](https://github.com/user-attachments/assets/81a8c619-c29c-44cf-b35a-c17b92399c4a)
|
![](https://github.com/user-attachments/assets/29a3d4b7-fc4d-4e2c-9795-793d089efde9)
|
![](https://github.com/user-attachments/assets/455d75ae-bba1-4b73-aa0b-350114b6227b)
|
### Video Background On-Pause/Window minimized (Custom Video 2160p)
| Mem: Current Release | Mem: This PR | VRAM: Current Release | VRAM:
This PR |
|----|----|----|----|
|
![](https://github.com/user-attachments/assets/8894a3f4-f67c-4d17-a008-189ad2299a75)
|
![](https://github.com/user-attachments/assets/4da743c4-ba44-4859-bd8f-b080bd61038b)
|
![](https://github.com/user-attachments/assets/1f1dcb14-5998-4ec3-b967-bd4cc2a1bd0f)
|
![](https://github.com/user-attachments/assets/a5101117-eb56-41c5-84da-ca28f1d02fcf)
|

- **[Fix]** Race Conditions in Sophon API Loader causing Available Game
Version reverted to Deprecated ZIP API, by @neon-nyan
- **[New]** Sync game settings to database by @bagusnl, @neon-nyan 
- Now users with DB subsystem set-up can backup their game settings to
the configured DB endpoint. Uses Collapse' way of saving registry so
storage shouldn't be a problem.
- Guide on setting Collapse' DB subsystem is here
https://collapselauncher.com/feature/database.html
- **[Imp]** DiscordRPC fixes and stability improvements by @neon-nyan 
- **[Imp]** Improved Collapse settings loading time by initializing
Waifu2x at startup time by @neon-nyan
- **[Imp]** Improved video background playback by reducing animation in
carousel by @neon-nyan
  - Should be smoother and less prone of frame skip now
- **[Imp]** Background CPU and Memory utilizations by @neon-nyan 
- CPU utilization should be next to 0 when Collapse is minimized (and to
tray) by hiding WinUI elements.
- Memory utilization is improved by running more aggressive
garbage-collections
- **[Fix]** Region without static images uses last loaded region
background resources by @neon-nyan


<details>
  <summary>Changelog Prefixes</summary>
  
  ```
    **[New]**
    **[Imp]**
    **[Fix]**
    **[Loc]**
    **[Doc]**
  ```

</details>
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.

1 participant