What's the fastest way to update an owner drawn control? - windows

I have an owner drawn control which displays data. The data changes over time. I need to find a fast way to continuously update the control.
I tried using timers, but the problem is that the timer alone runs at maximum of 65 frames per second. And if control's paint method takes time, the fps becomes quite low.
I tried requesting update (InvalidateRect) from control's paint method, this way it repaints the control hundreds of times per second, however the rest of the UI doesn't work properly (e.g. buttons not updated, tooltips are frozen, etc).
What's the proper way to update an owner drawn control as fast as possible, still keeping the UI responsive?
P. S. I need this to work with GDI, so I can't use OpenGL/Direct3D to display my data.

If you want to update it literally as fast as possible, run a message loop that doesn't wait for input. If you continue to process messages the UI will stay responsive but when there aren't any messages your control will continue to update.
Psuedo-code:
while (!fQuit)
{
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
DispatchMessage(&msg);
RepaintControl();
}
Depending on how your control is implemented, the "repaint" function might be as simple as:
RedrawWindow(hwndControl, 0, 0, RDW_INVALIDATE | RDW_UPDATENOW);
If you find this bogs your machine down too much (although with multi-core it should be ok) or is updating too quickly, you could use a waitable timer to get a higher-resolution update while still yielding control at times. See Using Waitable Timer Objects for details on waitable timers.

Related

what is the best approach to scheduling repaint for winapi window?

I already tried multiple ways of rendering simple animations on winapi windows using wingdi, and realized that its fairly slow even for primitive animations, and inconsistent if i do it by spamming SendMessageW when there are no events for this window. So I thought that instead of rendering when I can i should schedule it with a fixed interval hence improving framerate and quality of the animation. I now wondering if this can be done with registering some callback with windows or there is no such functionality and i do have to spin another thread to function as a timer? Is it generally a better idea? What is the commonly accepted way to do things like this, when event triggered redraws just wont cut it?
Concerning the scheduled animations, there is SetTimer() and KillTimer(), if I remember their names correctly. In any case, "timer" is what you're looking for.
Concerning the painting in the timer callbacks, don't. Instead, adjust animation parameters (positions, colours etc) and trigger a redraw using InvalidateRect(). This will in turn invoke the regular drawing event handler. The difference is that this will not waste any CPU if your window is hidden or minimized. Also, in any case, when the window is un-hidden, the drawing event handler has to be able to draw the right window content anyway.
There is a derived version of this where you update an image in a memory DC and only blit to the window in the drawing event handler. It's unclear whether that is necessary in your case though. It's only when drawing takes an extended amount of time and you want to keep your UI responsive.
Well as my luck usually goes i found this article on MSDN, and it has a very nice example. Putting it here if someone will have same question

What is the recommended frequency for UI changes?

I have a cocoa application window (NSWindow) which position on the screen should be updated frequently (depending on some calculation). As noticed in the documentation, UI changes should be made on the main thread:
void calculationThread()
{
while(true)
{
calculatePosition();
if(positionChanged)
{
dispatch_async(dispatch_get_main_queue(), ^{ setWindowPos(); });
}
}
}
void setWindowPos()
{
[window setFrame:_newFrame display:YES];
}
Now the problem I have is that the window movement is very slow and delayed. After making some profiling I see that the calculation process takes about 40mSec, meaning that I'm queueing up a backlog of UI updates 25 times a second.
I've read here that this might be faster than they can be processed and timer should be used to fire the changes every tenth of a second or so. But, wouldn't it be too slow for the human eye (I mean, in that case the movement wouldn't be delayed but would be lagged causing pretty much the same affect).
I will appreciate some knowledge sharing on this. Actually my main 2 questions are:
Are 25-30 UI updates per second really to much?
If yes, what is the recommended UI changes frequency?
The frequency at which a window can be moved around onscreen without problems will of course depend upon the speed of the user's machine, the video card they have, the size of the window, and probably a bunch of other factors. There is no single good answer to this. However, if you just drag a window around on your screen, you will notice that it can probably be moved very smoothly (unless your machine is very busy or very low on memory or something); I would not expect 25 times per second to produce a problem on a modern Mac. Not even close, in fact.
#RobNapier's points about Core Animation etc. are fine, but overstated I think; there is nothing inherently wrong with changing your UI using a timer or other periodic update if that is what you actually want to do. CoreAnimation is a toolkit for making some types of animation easier; using it is not required, and it is not suited to every problem. Similarly, if you want to make changes that are actually synched to screen refresh then CVDisplayLink is useful, but it doesn't really sound like that's what you want to do.
For your purposes, your basic approach seems fine, although I would suggest adding an NSDate check in order to skip updates if the previous update was less than, say, 1/60th of a second previous. After all, the calculation appears to take 40mSec on your machine, but it might be much faster on some other machine; you want to throttle your drawing to a reasonable rate just to be a good citizen.
So what is the problem, then? I suspect the issue might actually be your call [window setFrame:_newFrame display:YES]. If you look at Apple's docs for that method, they state "When YES the window sends a displayIfNeeded message down its view hierarchy, thus redrawing all views." Each time you call that method, then, you are not only moving your window (which I gather is your intention); you are redrawing all of the contents of the window, too, and that is slow. If you don't need to do that, then that is the overhead you need to eliminate. Call setFrameOrigin: or setFrameTopLeftPoint: instead (which make the semantics clear, that you are moving the window without resizing it or redrawing it), or perhaps just setFrame:display: passing NO instead of YES, and I'm guessing your performance problem will vanish.
If you do in fact need to redraw the window contents every time, then please edit the problem description to reflect that. In that case, the solution will have to involve profiling why your window drawing is slow, and figuring out ways to optimize that, which is an entirely different problem.
As you've discovered, you should never try to drive the UI from a tight loop. You should let the UI drive you. There are three primary tools for that.
For simple problems, AppKit is capable of moving windows around the screen. Just call [NSWindow setFrame:display:animate:]. You can override animationResizeTime: to modify the timing.
In many cases AppKit doesn't give enough control. In those case, the best tool is almost always Core Animation. You should tell the system using Core Animation how you where you want UI elements to wind up, and over what period and path, and let it do the work of getting them there. See the Core Animation Programming Guide for extensive documentation on how to use that. It focuses on animating CALayer, but the techniques are similar for NSWindow. You'll use [NSWindow setAnimations:] to add your animation. Look at the NSAnimatablePropertyContainer protocol (which NSWindow conforms to) for more information. For a simple sample project of animating NSWindow, see Just Say No from CIMGF.
In a few cases, you really do need to update the screen manually at the screen update frequency. I must stress how rare this situation is. In almost all cases, Core Animation is the correct tool. But in those rare case (some kinds of video for instance), you can use a CVDisplayLink to handle this. That will call you each time the screen would like to refresh, giving you an opportunity to update your content to match.

How to find out real screen refresh rate (not the rounded number)

According to this article by Microsoft the screen refresh rate set by the user can be (and is mostly) a fractional number. The user sets 59Hz but the screen runs according to the on screen display at 60Hz, but in reality it's 59.94Hz. What I need for a extremely smooth animation is the 59.94Hz.
Using IDirect3DDevice9::GetDisplayMode I only get an int value which cannot by definition represent the real timing (same goes for EnumDisplaySettings). I encounter a visible stutter about every second because it reports the rounded/truncated 59. If I manually correct the reported timing in my application to 59.94 it runs smooth.
Anybody knows how I can retrieve the real screen refresh rate?
My current workaround is mapping 60Hz and 59Hz both to constant 59.94Hz but that's not satisfying.
If you are targeting Windows Vista or later, the answer depends on the mode in which your app is running.
If it is a windowed app (or windowed full-screen), refresh rate is controlled via the Desktop Window Manager (DWM) according to user settings and other factors. Use DwmGetCompositionTimingInfo and look at DWM_TIMING_INFO::rateRefresh to get the monitor refresh rate.
If the app is true full-screen, then the full-screen swap chain you create overrides the system default. However, your selected refresh rate (DXGI_SWAP_CHAIN_FULLSCREEN_DESC::RefreshRate) should match one of the monitor-supported refresh rates. You can get the list of supported refresh rates using IDXGIOutput::GetDisplayModeList. Here's an example of how to do so:
UINT numModes = 0;
dxgiOutput->GetDisplayModeList(DXGI_FORMAT_B8G8R8A8_UNORM, 0, &numModes, NULL);
DXGI_MODE_DESC* modes = new DXGI_MODE_DESC[numModes];
dxgiOutput->GetDisplayModeList(DXGI_FORMAT_B8G8R8A8_UNORM, 0, &numModes, modes);
// see modes[i].RefreshRate
In any case, you shouldn't see glitching if you're triple-buffered. You should just present as fast as you can and the OS will present on time. If you combine triple-buffering with custom managed frame timing, you're guaranteed to not actually get triple-buffering, and you'll get glitches any time there's drift in the vblank phase (which happens gradually even if you have a perfect value for refresh rate). If you want to stick with triple-buffering, just present as fast as you can and let the OS take care of presentation timing. If you're using your own timing to drive Present()s (for example, to get low-latency response), you should throw in a call to IDXGIOutput::WaitForVBlank on another thread to help synchronize frame timings. If you end up doing that, you should also use IDXGISwapChain::GetFrameStatistics to make sure you recover from any spurious glitches, otherwise you'll end up a frame behind.
Good luck!

InvalidateRect called too frequently blocks other windows from redrawing

I develop audio plugins, which are run inside their hosts and work realtime. Each plugin has its own window with controls, which often contains some kind of analysis pane, a pretty big rectangle that gets repeatedly painted (e.g. 20-50x per second). This is all working well.
The trouble comes when the user adjusts a parameter - the plugin uses WM_MOUSEMOVE to track mouse movements and on each change calls ::InvalidateRect to make the relevant portion of the window be redrawn. If you move quickly enough, the window really gets quickly repainted, however there seems no time for the host and other windows to be redrawn and these usually perform some kind of analysis feedback too, so it is really not ideal.
No my questions:
1) Assuming the host and other window are using ::InvalidateRect too, why mine is prioritized?
2) How to make ::InvalidateRect not prioritized, meaning the window needs to be invalidated, but it may be later, the rest of the system must get time for their redrawing too.
Thanks in advance!

Can a window be always on top of just one other window?

In Windows, is it possible to set window A such that it is always on top of window B, yet allow other windows to work as normal and appear over the top of both, when active.
In other words, I want a parent-child relationship between two windows. Can this be done without making window A a child of window B, MDI-style? Window B isn't mine (Internet Explorer), and screws my dialog A's graphics up when I try to achieve this with SetParent.
I thought I'd cracked it with this idea from an MSDN forum post, but alas windows A is still always on top of everything, not just window B.
// Place window A on top
SetWindowPos(hWndWindow, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
// Place window B underneath it
SetWindowPos(hWndParent, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE);
Is it possible?
Wouldn't creating an ownership relationship do the trick?
SetWindowLong(hwndChild, GWL_HWNDPARENT, hwndOwner)
The windows can be in different processes and you can call this from any process. This will ensure that the child window is always above the owner window. This is different than SetParent which actually creates a Parent / Child relationship. Read through this article (its from 1993 but still mostly correct) to see the distinction between ownership and parenting.
When your window's Z-order (or size or position) is changing, it should receive a WM_WINDOWPOSCHANGING message. If you process that message, you have an opportunity to modify the final Z-order (or size or position) to which the window is moved.
To illustrate, in hWndA's window procedure:
case WM_WINDOWPOSCHANGING:
DefWindowProc(hWnd, msg, wParam, lParam);
WINDOWPOS *p = (WINDOWPOS*)lParam;
p->hwndInsertAfter = hWndB;
p->flags &= ~SWP_NOZORDER;
return 0;
should insert hWndA after hWndB in the Z-order any time hWndA's position changes.
Until Vista, one way to do it would have been to use SetWindowsHookEx, and hook the WH_CBT or WH_CALLWNDPROC hook, and then take appropriate action when you detect the Z order changing. However this doesn't work with Vista (as far as I can tell from googling).
The only other solution I can think of is to set up a timer to fire every few seconds, and then when you receive a WM_TIMER, you interrogate the system using GetNextWindow to find out which window is behind yours. If it's not IE, then call SetWindowPos to position your window above IE (I assume you have a HWND for the IE window you care about - remember there can be multiple IE windows).
This will cause problems if people try to bring your window to the front - it will flip back to being just above IE. In this case, in your code you could handle WM_ACTIVATE and try to change the Z-order of IE's window so it's below your window (call SetWindowPos to move IE's window so it's above the window that is currently below your window). This solution may be fraught with problems as Windows may try to prevent you messing with the windows of another process, for security reasons. On the other hand, the MSDN docs for SetWindowPos don't explicitly mention that you can't manipulate the windows of another process. There may be obscure limitations though.
Even with this timer hack, you're going to effectively have a busy-waiting loop in your app (with the frequent WM_TIMER messages) and this is generally a bad thing to do, especially for battery life of laptops etc. (because you prevent the CPU from entering a sleep state, and so on).
I'd say there's no good way of doing this, and anything you're likely to get working will be brittle and cause problems. I strongly recommend not trying to do it. Is it possible to make your program into some kind of plug-in or toolbar for IE instead?
NB Be particularly aware that SetWindowsHookEx imposes a performance penalty at a system-wide level if you go down this route.
Maurice's answer is the best out of what's here but is missing an important step. When you call show on your window that you want as the overlay, you need to call the show method that has the parameter. You'll need to define a class that implements the IWin32Window interface and just make a new instance of that. The only thing that interface cares about is the Handle so just set that to the handle of the IE window and it should work pretty well
If the parent-child relationship is made by yourself with the SetWindowPos() function, your desire can be implemented.
Can you access the Z-order of the windows?
I cannot recall the default z-order of windows, but I think it is 1. You might be able to set IE to a -1 and your app to 0.
Try this:
// Place window A on top of window B
SetWindowPos(hWndA, hWndB, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
The second window handle parameter specifies the next window down in the Z order.
Note this doesn't actually change the window parent-child relationships - but you can simulate it.

Resources