Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to smooth ugly jitter/flicker/jumping when resizing windows, especially dragging left/top border (Win 7-10; bg, bitblt and DWM)?

THE PROBLEM: When I grab the resize border of my Windows app, especially the top or left borders, and resize the window, the contents of the window do resize "live" as I drag, but they resize in a hideous manner that looks like a blatant bug to even the most novice user: the contents at the opposite edge of the window from the edge I am dragging jitter/flicker/jump back and forth wildly. Depending on the situation, the phenomenon may look like:

  • contents that seem to walk off the edge of the window and snap back when we slow down or stop dragging
  • contents that seem to pull into the window, intermittently displaced by a border of varying colors, often black or white
  • a seriously ugly "double image" with two overlapping copies of the content displaced by a distance proportional to how much/how fast we are dragging

The ugly phenomenon stops as soon as I stop dragging, but during the dragging it makes the app look amateurish and unprofessional.

It is not an understatement to say this Windows problem has driven thousands of app developers crazy.

Here are two example pictures of the phenomenon, kindly prepared for a related question by Roman Starkov:

Jitter:
example 1: jitter

Border:
example 2: border

Another example showing the evil "double image" phenomenon (note the quick flash) from Kenny Liu:

example 2: double-image

Another example video of the phenomenon with Task Manager is here.

THE QUESTION: Any developer who has experienced this problem quickly finds that there are at least 30 Stack Overflow questions, some recent and some dating from 2008, full of promising-sounding answers that rarely work. The reality is that this one problem has many causes, and the existing Stack Overflow questions/answers never make the wider context clear. This question seeks to answer:

  • what are the most likely causes of this kind of ugly jitter/flicker/jumping?
  • how do I tell which cause I am seeing?
  • is this cause specific to particular graphics drivers or general for Windows?
  • how do I fix each cause? can an app fix it?

(This is meant as a canonical Q&A to explain all the different causes of window resize jitter so that users can identify which of the causes is causing their problem and solve it. As the answers explain, all the permutations above (native/managed, window/dialog, XP-10) boil down to only two root causes, but identifying which you have is the tricky part.)

SCOPE OF THIS QUESTION: For the scope of this question, the phenomenon happens with:

  • both native Win32 and managed .NET/WPF/Windows Forms apps
  • both normal Win32 windows and Win32 Dialog windows
  • Windows versions including XP, Vista, 7, 8, and 10 (but see below for the dark truth of multiple causes)

NOT IN SCOPE OF THIS QUESTION:

  • If your app has one or more child windows (child HWNDs), the info in this question is useful to you (since the jerk-causing BitBlts we will describe are applied to your child windows along with the parent window), but during window resize you have an additional problem to handle that is beyond the scope of this question: you need to make all your child windows move atomically and in sync with the parent window. For this task, you will probably want BeginDeferWindowPos/DeferWindowPos/EndDeferWindowPos and you can find out about them here and here.

  • This question assumes that if your app draws to a window using GDI, DirectX, or OpenGL, then you have already implemented a WM_ERASEBKGND handler in your wndproc that simply returns 1. WM_ERASEBKGND is an arcane Windows remnant from Windows 3.1 that comes before WM_PAINT to give your app a chance to "erase the background" of your window before you draw your window...uh huh. If you let the WM_ERASEBKGND message go into DefWindowProc(), that will cause your entire window to get painted a solid color, usually white, on each redraw, including redraws that happen during live window resizing. The result is an ugly full-window flicker that is gross, but not the type of jitter/flicker/jumping we are talking about in this question. Intercepting WM_ERASEBKGND fixes this problem immediately.

  • This question is primarily about live-resize by dragging window borders with the mouse. However, much of what is written here also applies to ugly artifacts you can see when an app manually does a one-time window resize using SetWindowPos(). These are less visible though because they only flick on the screen for one instant, rather than over a long period of dragging.

  • This question is not about how to make your app-specific drawing code go faster, even though doing so may be a solution to the ugly resizing problem in many cases. If your app really does take huge amounts of time to redisplay its contents during live window resize, consider optimizing your drawing code in general or at least switching to a faster, lower-quality drawing mode during resize by intercepting the WM_ENTERSIZEMOVE/WM_EXITSIZEMOVE messages to detect resize.

  • If your app fails to resize at all during app resizing (e.g. it "hangs" during resizing, especially if it is OpenGL using GLFW or other library), see these other questions which explain about Microsoft's hideous nested/modal event loop inside WM_SYSCOMMAND during dragging: here especially this good answer, here, here, here, and here.

like image 337
Louis Semprini Avatar asked Oct 26 '18 01:10

Louis Semprini


People also ask

How do I resize the client area when shrinking the window?

If you're using the top or left border, your client area contents will move along with the top left of the client area. In order to get any live resizing when shrinking the window, you have to manually draw from a wndproc message like WM_SIZE, or call InvalidateWindow () to trigger a later WM_PAINT.

Is there a way to get rid of jitter in Windows?

There is a second hack, and it is an incredibly creative one: as explained in the StackOverflow post Can't get rid of jitter while dragging the left border of a window, you can actually create two main windows in your app, and every time Windows would do SetWindowPos, you intecept that and instead hide one window and show the other!

Why does my app take so long to redisplay during window resize?

If your app really does take huge amounts of time to redisplay its contents during live window resize, consider optimizing your drawing code in general or at least switching to a faster, lower-quality drawing mode during resize by intercepting the WM_ENTERSIZEMOVE/WM_EXITSIZEMOVE messages to detect resize.

How do I fix the resize problem with bitblts?

So, most solutions for fixing resize problems involve disabling the BitBlt. If you implement a WM_NCCALCSIZE handler and that handler returns WVR_VALIDRECTS when wParam is 1, you can actually control which pixels Windows copies ( BitBlts) from the old client area and where Windows places those pixels in the new client area.


2 Answers

PART 3: Gallery of Sorrow: Annotated List of Related Links

You might be able to glean ideas I missed by looking over the source material:

2014 with 2017 updates: Can't get rid of jitter while dragging the left border of a window : probably the most up-to-date question but still lacks context; suggests a creative but rather crazy hack of having two windows and alternately unhiding them during live resize! Also the only question I have found with an answer mentioning a race condition in DWM and a partial timing fix with DwmGetCompositionTimingInfo().

2014 Why is there a black lag every time a WPF window is resized? : yes WPF does it too. No useful answers

2009 How to fix the WPF form resize - controls lagging behind and black background? : controls lagging behind and black background?" multi-HWND example. mentions WM_ERASEBKGND and background brush tricks, but no modern answer.

2018 Is there a way to reduce or prevent form flickering when using WPF? : yes, still not fixed as of 2018.

2018 Reduce flickering when using SetWindowPos to change the left edge of a window : unanswered question that got many obsolete recommendations like WM_NCCALCSIZE

2012 OpenGL flickering/damaged with window resize and DWM active : good statement of problem, answerers completely misunderstood the context and provided inapplicable answers.

2012 How to avoid transient updates in a GUI resize? : mentions the trick of intercepting WM_WINDOWPOSCHANGING and setting WINDOWPOS.flags |= SWP_NOCOPYBITS.

2016 Unity bug report: "Window resizing is very choppy and stutters (border does not smoothly follow the mouse)" typical bug report found in hundreds of apps that is partially due to the problem in this bug report, and partially due to certain apps having slow drawing. The only doc I EVER found which actually says that Windows 10 DWM clamps and extends the outer pixel of the old window, which I can confirm.

2014 Flickering on window when resizing from left side with pre-Windows-8 answer including CS_HREDRAW/CS_VREDRAW and WM_NCCALCSIZE.

2013 Resizing Window causes smearing near the right border with old-school Win-7-only solution to disable Aero.

2018 Flicker-free expansion (resize) of a window to the left an example of a multi-window (multi-HWND) case, no real answer.

2013 WinAPI C++: Reprogramming Window Resize : too ambiguously asked to tell whether it is about client-area flickering (like this question) or non-client-area flickering.

2018 GLFW bug "Resizing windows on Windows 10 shows jumpy behaviour" one of MANY such bugs which never explain the context, like many StackOverflow posts

2008 "Flicker Free Main Frame Resizing" CodeProject that actually does a StretchBlt but won't work in a Windows 8+ world, where app does not have control when incorrect pixels are shown on screen.

2014 Smooth window resizing in Windows (using Direct2D 1.1)? : Well-stated but unanswered issue with Windows 8+ DWM copy

2010 How do I force windows NOT to redraw anything in my dialog when the user is resizing my dialog? : WM_NCCALCSIZE fix to disable bitblt that no longer works in Windows 8+ since DWM corrupts the screen before app has chance to display.

2014 Flicker when moving/resizing window : roundup of previous fixes that do not work in Windows 8+.

2007 WinXP-era "reducing flicker" CodeProject recommending WM_ERASEBKGND+SWP_NOCOPYBITS

2008 early Google Bug report of new Vista DWM problems

like image 50
Louis Semprini Avatar answered Oct 17 '22 22:10

Louis Semprini


Table of Contents

Because this is a complex, multi-faceted issue, I recommend reading the answers in this order:

  • PART 1: What Makes Resize Look Good or Bad?

  • PART 2: Identifying and Fixing Windows Resize Problems

    • 2a: Resize Problems from SetWindowPos() BitBlt and Background Fill
    • 2b: Resize Problems from DWM Composition Fill
    • 2c: How to Diagnose Your Problem

as well as a list of source material which may help others glean insights:

  • PART 3: Gallery of Sorrow: Annotated List of Related Links

Please feel free to contribute more answers with creative ways of avoiding the problems described in 2a and especially 2b!

like image 10
Cody Gray Avatar answered Oct 17 '22 22:10

Cody Gray