Skip to content

Conversation

@neon-nyan
Copy link
Member

@neon-nyan neon-nyan commented Dec 15, 2025

Main Goal

As described by the title, this PR contains major changes to Collapse Launcher's Image/Video Background mechanism to match current behavior on HoYoPlay which have multiple sequence of background and layered images.

Here's the current list of what have been implemented:

  • Rewrite in-code based element to custom XAML Control-based element.
  • Implement a replacement for PipsPager (Implemented as: NewPipsPager)
  • Implement a replacement for FlipView (Implemented as PanelSlideshow)
    This implementation of a Slideshow element added some customization which FlipView can't accommodate, including:
    • Support for custom styling for navigation buttons.
    • Support for customizing the position for navigation buttons and margins, not only based on "Horizontal/Vertical" orientation.
    • Support for adding Transition animation for navigating between elements.
    • Support for pausing/playing video background.
    • Support for automatically switching to the next element based on its SlideshowDuration.
  • Implement a replacement for code-based background loaders (Implemented as: LayeredBackgroundImage)
    This custom control has major improvements than its previous background loading mechanism, including:
    • Built-in external decoder support for unsupported WIC (Windows Imaging Component) formats, including .webp, .avif and .jxr via PhotoScaler library.

    • Using direct Bitmap copy for drawing video frames via native ISurfaceImageSourceNativeWithD2D.

      Previously, Collapse Launcher has to rely on software bitmaps to draw each frames into the UIElement to pulls of the XAML-effects and compositions, including XAML Blur.

      The reason why Collapse Launcher has to perform this multiple frame copy is because MediaPlayer, by default, doesn't support XAML effects and Composition and instead, each frames were drawn directly into different pipeline of IDXGISwapChain which decoupled from the main WinUI's, causing the element above it feels a bit off (including no acrylic effects for any elements above it)

      This causes few overhead, in-terms of memory allocation or CPU cycles as it requires to perform multiple buffer copy steps:

      • Allocate SoftwareBitmap (CPU)
      • Copy MediaPlayer's frame buffer into SoftwareBitmap (GPU -> CPU)
      • Initialize drawing session from VirtualCanvasImageSource as source for Image to copy from SoftwareBitmap (GPU)
      • Copy SoftwareBitmap buffer into VirtualCanvasImageSource (CPU -> GPU)
      • Invalidate and draw buffers into Image's surface (GPU)

      Now, thanks to huge changes on Hi3Helper.Win32 submodule (and documentations provided by Win2D and DirectNAot), we managed to copy the frames from MediaPlayer directly into backed-VirtualSurfaceImageSource/ISurfaceImageSourceNativeWithD2D pipeline, loaded as Image's source, with XAML-effects and Composition support and without additional buffer copy on CPU side. Now, the process only require these steps:

      • Calls ISurfaceImageSourceNativeWithD2D->BeginDraw to create IDirect3DSurface drawing session (GPU)
      • Copy MediaPlayer's frame buffer into IDirect3DSurface (GPU -> GPU)
      • Invalidate and draw buffers into Image's surface via ISurfaceImageSourceNativeWithD2D->EndDraw (GPU)

TODO List:

  • Fix memory leak by completely disposing MediaPlayer, ISurfaceImageSourceNativeWithD2D and underlay ID3D11Device + ID2D1Device
  • Move all code to Collapse Launcher's main project.
  • Adjust custom background mechanism with these changes
  • [Optional] Use fully-asynchronous drawing session on ISurfaceImageSourceNativeWithD2D (Detaching draw session from UI-thread). Currently not possible due to SurfaceImageSource thread model bound to the UI-Thread and not set to Multi-thread mode.

PR Status :

  • Overall Status : In Progress
  • Commits :In Progress
  • Synced to base (Collapse:main) : Yes
  • Build status : OK
  • Crashing : No
  • Bug found caused by PR : 1 (Unmanaged Memory Disposal)

Templates

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

+ Fix UI layout resized incorrectly
+ Fix UI state updated incorrectly
+ Cleaning-up constants and unused/duplicate methods
+ Add option to select pip by hovering instead of clicking
+ Add event handlers for ItemIndex and ItemsCount changes
+ By preloading it to an empty grid, this issue can be fixed. Yeah, for no reason, even when the element has already been added to the ContentPresenter, the first frame of the element will report the offset and size to 0x0. So by tricking it to load into an empty grid, the element will force itself to load and reports the correct offset and size.
The countdown is currently using Timer for counting down the slideshow. This approach, however, uses interval event on each ticks so the duration of the count might be inaccurate (but not significant). The tick resolution is 100ms for now.
+ CodeQA
+ Simplify content placement on XAML
+ Fade-in/out countdown bar on entering and exiting
+ Fix content not updating while adding/removing items from Items collection
+ Implements Parallax effect on mouse hover
+ The image is currently using a dummy one and not yet compositing. Will be fully implemented later (ETA: at the end of this week, hopefully)
+ Bug fix on object -> double conversion, causing throw
+ Add IMediaCacheHandler interface for handling cache on media loading
Now we are able to perform direct frame drawing without dependency on CanvasBitmap or any SoftwareBitmap which requires back-to-back GPU->CPU->GPU frame copy. The GPU usage difference is negligible, as close as mainstream video player GPU usage.
@bagusnl
Copy link
Member

bagusnl commented Dec 15, 2025

Fatal crash on region change
Sentry issue: https://collapse.sentry.io/issues/83096126/?query=is%3Aunresolved&referrer=issue-stream&sort=date
trace waterfall https://collapse.sentry.io/issues/trace/726250839f0a4197853614549bcebdbf/?fov=0%2C8187&groupId=83096126&node=error-22379c9e57cd4faabbb35d1e57198a8e&pageEnd=2025-12-16T05%3A31%3A08.002&pageStart=2025-12-15T05%3A31%3A08.002&project=4508302961410128&query=is%3Aunresolved&referrer=issue-stream&sort=date&source=issue_details&tab=waterfall&timestamp=1765819866.017

[Error]   [XAML_OTHER] Sender: CollapseLauncher.App
          System.ObjectDisposedException: Cannot access a disposed object.
          Object name: 'Microsoft.Win32.SafeHandles.SafeFileHandle'.
             at System.Runtime.InteropServices.SafeHandle.DangerousAddRef(Boolean&) + 0x3b
             at Interop.Kernel32.WriteFile(SafeHandle, Byte*, Int32, Int32&, NativeOverlapped*) + 0x4b
             at System.IO.RandomAccess.WriteAtOffset(SafeFileHandle, ReadOnlySpan`1, Int64) + 0x62
             at System.IO.RandomAccess.<>c.<WriteAtOffsetAsync>b__21_0(ValueTuple`4 state) + 0x105
             at System.Threading.AsyncOverSyncWithIoCancellation.<InvokeAsync>d__7`1.MoveNext() + 0x16d
          --- End of stack trace from previous location ---
             at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16) + 0x1d
             at System.IO.Strategies.BufferedFileStreamStrategy.<FlushAsyncInternal>d__55.MoveNext() + 0x284
          --- End of stack trace from previous location ---
             at System.IO.Strategies.BufferedFileStreamStrategy.<DisposeAsync>d__27.MoveNext() + 0x120
          --- End of stack trace from previous location ---
             at System.IO.Strategies.BufferedFileStreamStrategy.<DisposeAsync>d__27.MoveNext() + 0x28d
          --- End of stack trace from previous location ---
             at System.IO.FileStream.<DisposeAsync>d__57.MoveNext() + 0x8e
          --- End of stack trace from previous location ---
             at Hi3Helper.EncTool.CopyToStream.<DisposeAsync>d__38.MoveNext() + 0xf7
          --- End of stack trace from previous location ---
             at CollapseLauncher.Pages.UrlToCachedImageSourceConverter.<>c__DisplayClass7_0.<<LoadLazyBitmap>g__ImageOnImageOpened|0>d.MoveNext() + 0x8a
          --- End of stack trace from previous location ---
             at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__124_0(Object state) + 0x1a
             at Microsoft.UI.Dispatching.DispatcherQueueSynchronizationContext.<>c__DisplayClass2_0.<Post>b__0() + 0x19
          Current Stacktrace:    at CollapseLauncher.App.<>c.<.ctor>b__0_2(Object sender, UnhandledExceptionEventArgs e) + 0x1fa
             at ABI.Microsoft.UI.Xaml.UnhandledExceptionEventHandler.Do_Abi_Invoke(IntPtr, IntPtr, IntPtr) + 0x6e
             at WinRT.ExceptionHelpers.ReportUnhandledError(Exception) + 0x69
             at Microsoft.UI.Dispatching.DispatcherQueueSynchronizationContext.<>c__DisplayClass2_0.<Post>b__0() + 0x29
             at ABI.Microsoft.UI.Dispatching.DispatcherQueueHandler.Do_Abi_Invoke(IntPtr) + 0x46
             at ABI.Microsoft.UI.Xaml.IApplicationStaticsMethods.Start(IObjectReference, ApplicationInitializationCallback) + 0x89
             at CollapseLauncher.MainEntryPoint.Main(String[] args) + 0x5ec

@Cryotechnic Cryotechnic requested review from a team and Copilot December 15, 2025 17:34
@neon-nyan
Copy link
Member Author

Fatal crash on region change Sentry issue: https://collapse.sentry.io/issues/83096126/?query=is%3Aunresolved&referrer=issue-stream&sort=date

[Error]   [XAML_OTHER] Sender: CollapseLauncher.App
          System.ObjectDisposedException: Cannot access a disposed object.
          Object name: 'Microsoft.Win32.SafeHandles.SafeFileHandle'.
             at System.Runtime.InteropServices.SafeHandle.DangerousAddRef(Boolean&) + 0x3b
             at Interop.Kernel32.WriteFile(SafeHandle, Byte*, Int32, Int32&, NativeOverlapped*) + 0x4b
             at System.IO.RandomAccess.WriteAtOffset(SafeFileHandle, ReadOnlySpan`1, Int64) + 0x62
             at System.IO.RandomAccess.<>c.<WriteAtOffsetAsync>b__21_0(ValueTuple`4 state) + 0x105
             at System.Threading.AsyncOverSyncWithIoCancellation.<InvokeAsync>d__7`1.MoveNext() + 0x16d
          --- End of stack trace from previous location ---
             at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16) + 0x1d
             at System.IO.Strategies.BufferedFileStreamStrategy.<FlushAsyncInternal>d__55.MoveNext() + 0x284
          --- End of stack trace from previous location ---
             at System.IO.Strategies.BufferedFileStreamStrategy.<DisposeAsync>d__27.MoveNext() + 0x120
          --- End of stack trace from previous location ---
             at System.IO.Strategies.BufferedFileStreamStrategy.<DisposeAsync>d__27.MoveNext() + 0x28d
          --- End of stack trace from previous location ---
             at System.IO.FileStream.<DisposeAsync>d__57.MoveNext() + 0x8e
          --- End of stack trace from previous location ---
             at Hi3Helper.EncTool.CopyToStream.<DisposeAsync>d__38.MoveNext() + 0xf7
          --- End of stack trace from previous location ---
             at CollapseLauncher.Pages.UrlToCachedImageSourceConverter.<>c__DisplayClass7_0.<<LoadLazyBitmap>g__ImageOnImageOpened|0>d.MoveNext() + 0x8a
          --- End of stack trace from previous location ---
             at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__124_0(Object state) + 0x1a
             at Microsoft.UI.Dispatching.DispatcherQueueSynchronizationContext.<>c__DisplayClass2_0.<Post>b__0() + 0x19
          Current Stacktrace:    at CollapseLauncher.App.<>c.<.ctor>b__0_2(Object sender, UnhandledExceptionEventArgs e) + 0x1fa
             at ABI.Microsoft.UI.Xaml.UnhandledExceptionEventHandler.Do_Abi_Invoke(IntPtr, IntPtr, IntPtr) + 0x6e
             at WinRT.ExceptionHelpers.ReportUnhandledError(Exception) + 0x69
             at Microsoft.UI.Dispatching.DispatcherQueueSynchronizationContext.<>c__DisplayClass2_0.<Post>b__0() + 0x29
             at ABI.Microsoft.UI.Dispatching.DispatcherQueueHandler.Do_Abi_Invoke(IntPtr) + 0x46
             at ABI.Microsoft.UI.Xaml.IApplicationStaticsMethods.Start(IObjectReference, ApplicationInitializationCallback) + 0x89
             at CollapseLauncher.MainEntryPoint.Main(String[] args) + 0x5ec

As mentioned above, the code for these changes haven't been applied to main Collapse Launcher code yet and still on BackgrounTest project. This is expected as it still uses existing implementation for background loaders

@bagusnl bagusnl review requested due to automatic review settings December 15, 2025 17:42
@bagusnl
Copy link
Member

bagusnl commented Dec 15, 2025

As per @neon-nyan statement above, copilot code check is removed until PR is actually finished in Collapse side

Cc @Cryotechnic

+ This also occurs during element ``Unloaded`` event
+ The loader will perform media seeking to the last position if the previous source was the same
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.

3 participants