-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Description
Hi,
I've been tackling the well-known BitmapCache issue involving multiple windows in WPF. I've delved deeply into the problem, examining WPF internals down to the native core. I'm actively working on and addressing the issue, You can find the code at this repository.
What is happening, How it's happening
To reproduce this bug, you need to create at least two windows. Let's designate the first window as the "Prime Window" and subsequent windows as "Alternative Windows". Adding a BitmapCache to any Alternative Window created after the Prime Window leads to a visual freeze on those Alternative Windows after pressing Alt + Ctrl + Del, locking the screen, or prompting UAC. Let's refer to this event as the "Display Reset".
Following a Display Reset, hovering over a reactive control (such as a Button or Checkbox) on the Prime Window triggers a WM_PAINT message. However, on the Alternative Windows, the WM_PAINT fails, causing the operating system's graphic manager to continually send (spam) WM_PAINT messages to the Alternative Windows. This incessant messaging is the root cause of the lock and lag experienced on Alternative Windows. It's suspected that the presence of a BitmapCache element causes the painting to fail and return false.
Previous Solutions Provided by Community :
-
Terminating DWM.exe resolves the lock issue, It's a very ugly and dirty way to fix it, But it works.
-
Switching to software rendering and disabling Hardware Acceleration has been suggested. However, this is deemed undesirable due to the necessity of GPU usage in 2024.
Current Findings and Proposed Solutions :
-
Applying a
BitmapCacheto an element in the Prime Window prevents lock on Alternative Windows after a Display Reset. [Branch] -
Hovering over a single reactive control (such as a Button or Checkbox) on the Prime Window with the mouse rectifies the lock on Alternative Windows, without necessitating any
BitmapCacheon the Prime Window. [Branch] -
Closing the Prime Window promotes the second window created after it to the status of Prime Window, resolving any lock situation it may be in.
-
To detect Display Reset, utilize a
D3DImageand listen for theIsFrontBufferAvailableChangedevent. Upon Display Reset, switchProcessRenderModeto software and back to hardware, It will effectively resolve the problem. However, this approach may introduce visual glitches that I personally do not like. [Details]
MediaControl Solution (Cross-Version) :
MediaControl is an internal component of Milcore (WPF Backend Render Engine) which controls rendering options in a running WPF application. Although primarily designed for debugging and profiling, it offers a potential solution to this issue.
My current solution involves hooking MediaControl within your WPF application, manipulating rendering options, and setting the DisableDirtyRegionSupport option to true. By disabling dirty region support, the bitmap cache problem disappears. However, this solution may lead to increased resource usage and impact performance since the entire WPF window will be rendered instead of only the changed areas. [Implementation]
In this solution, we connect to a shared memory path created per process at wpfgfx_v0400-<PID>, then modify the structure to change the DisableDirtyRegionSupport flag. I'm actively working on a fully managed approach to eliminate the dependency on milctrl_v0300_x64.dll.
Note: To make this solution work you need to change
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Avalon.Graphics\EnableDebugControlto 1, If it doesn't be set wpfgfx doesn't create MediaControl. You can also simply hookRegQueryValueExand return 1 when wpfgfx ask for the registry key.
Real-Time Patcher Solution (Specific-Version) :
In this solution, Instead of using MediaControl to disable Dirty Region Support we directly patch it in wpfgfx. It's stored as g_fDirtyRegion_Enabled inside wpfgfx dll file, To find the offset for installed version you need to download pdb of wpfgfx and use OffsetExtractor to find the static offset of the value.
By using this method we can directly disable Dirty Region Support, Unlock, Restore it back to enabled. [Implementation]
Note: I will update this section with any further discoveries or refinements in solution.