This article will demonstrate how to access the power of Vista's new hardware accelerated windowing system without "managed" languages and best of all, without any use of .NET at all.
The application provided uses good old unmanaged C++ and DirectX 9 Ex to create an anti-aliased, semi-transparent, 3D spinning cube on an Aero Glass window that operates as smoothly as any other application under Vista.
I am not a university trained programmer nor am I a DirectX expert. I am self-taught; a computer enthusiast who has just become very interested in DirectX.
I have been waiting for a hardware accelerated interface in Windows for a very long time. Finally it arrives and I'm bombarded with information from all over the internet implying that I won't be able to leverage this hardware accelerated interface without using .NET and the Windows Presentation Foundation (WPF).
I am a control freak when it comes to programming and as such I am not a fan of .NET and/or managed programming languages. The idea that I would be forced to use .NET to do anything special in Vista did not sit well with me at all so I set about finding as much information as possible about how Vista does what it does so that I could then bypass .NET. It turns out that Microsoft has not hidden the functionality from unmanaged developers. They have just done a very good job of implying that .NET and WPF is the only answer and to reinforce that implication, have provided no information (that I could find) on how to do anything without .NET and WPF.
The WPF provides some rather cool functionality for developers. Finally we get the features of GDI+ but with hardware acceleration and in addition WPF even introduces the concept of true 3D controls. Both of these features are excellent but I'll be damned if I'm going to use a managed language and .NET to get them.
I began my search for information by hunting through Microsoft developer blogs. After some time I came across the blog of one Greg Schechter a member of the Desktop Window Manager (DWM) development team. Greg has shared a lot of very useful information about Vista technologies but most importantly he has described in reasonable detail how the DWM works. His blog gave me enough information to believe this could most certainly be done.
The DWM is the application that provides the hardware accelerated windowing system in Vista. To be short and sweet, the DWM is a full screen Direct3D app that creates a textured quad for every "window", arranges these quads in 3D space, and renders them to the screen along with some sexy pixel shader effects (Aero Glass). The standard desktop view appears to be good old 2D but a quick hit of the WinKey+TAB will demonstrate that this is definitely not the case.
Microsoft has managed to give us this new windowing environment without breaking any previous software (well no software that followed the Win32 design rules anyway). You can write a Vista app using the standard Win32 libraries and it will just work exactly as you would expect it to. Ten points to Microsoft for that achievement and negative ten points for leaving non .NET developers in the dark about how to do what WPF does.
The vast majority of the programming examples I have found on the net targeting Vista use the .NET framework, a few use older class libraries like MFC or WTL, and fewer still use good old vanilla Win32. On the custom graphics side of the equation it's either WPF or GDI+ for special effect programming like true alpha blended graphics etc. Not one example have I found that uses Direct3D; the very system which the WPF and DWM use natively. My intention is to change that.
At first glance this example application may seem unimpressive; "Oh yay it's a spinning cube on a window". Stop being unimpressed for a moment and consider that what we have here is a Direct3D rendered spinning cube along with anti-aliasing and graded translucency being composited seamlessly through a real time generated alpha channel. A little more impressed now? No? Well then, stop and think for few more minutes and realize that what this application demonstrates is the very small tip of an infinitely large iceberg. Instead of being limited by .NET and WPF's capabilities, you are only limited by DirectX and that, my friends, is huge.
So, without further ado, let's get down to the nitty gritty.
The application source provided is as simple as possible. It has some error checking but any error will just cause a silent fail. Anything more complicated than that would over-complicate the code. I have only injected small snippets of code into the article that are directly related to the technique being described. Study the downloadable source code for more detail.
Getting what we want is simply a matter of setting up our window and Direct3D in the correct way and the DWM will take care of the rest. There is no undocumented API to reverse engineer, no "leet" hacks, just the right things in the right places, at the right times.
The first thing we do is create a window the same way we always have. Declare and fill out a
WNDCLASSEX structure, register it, and create a window.
The first parameter of the
CreateWindowEx call contains an important factor that is essential to getting all of this to work.
hWnd = CreateWindowEx(WS_EX_COMPOSITED, g_wcpAppName, g_wcpAppName, WS_POPUP | WS_SIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, g_iWidth, g_iHeight, NULL, NULL, hInstance, NULL);
The key factor here is the
WS_EX_COMPOSITED extended window style. If you do not supply this flag your window will lag horribly when it is moved around, more on that later. The
WS_SIZEBOX window styles give us our simple window surface with a standard border.
I have used dwmapi.dll to extend the non-client area of our window to cover the entire area. There are plenty of other articles detailing this simple technique, so I won't explain it here.
Now that we have a window, we can get Direct3D up and running. The important things here are the specification of a back buffer format with an alpha channel and obviously setting the
Windowed flag. We do a little hardware checking for anti-aliasing availability and set the highest quality we can.
if(FAILED(Direct3DCreate9Ex(D3D_SDK_VERSION, &g_pD3D))) return E_FAIL;
pp.Windowed = TRUE;
pp.SwapEffect = D3DSWAPEFFECT_DISCARD; pp.BackBufferFormat = D3DFMT_A8R8G8B8;
pp.MultiSampleType = D3DMULTISAMPLE_NONMASKABLE;
pp.MultiSampleQuality = msqAAQuality - 1;
pp.MultiSampleType = D3DMULTISAMPLE_NONE;
))) return E_FAIL;
When a window and associated DirectX objects are configured correctly, something a little magical happens. It would seem that given the right conditions the DWM will decide that you know what you are doing and instead of creating a new back buffer at device creation time, will instead give you the buffer (texture) it created to render your window to. So you are directly rendering to the texture that the DWM will map onto a quad in the 3D space that is the Vista desktop. When the
WS_EX_COMPOSITED extended window style is removed all of a sudden, our window is jerky when compared to other Vista windows due to a double dose of Direct3D action. Every frame you are rendering is being rendered to your off screen back buffer which in turn is being copied into your "window" which in fact is actually a DWM managed texture which then gets mapped to a quad and composited with the rest of the windows on the screen.
I could well be wrong about the above but at the moment this is my best guess as to what is going on based on information gleaned from several MS developer blogs. If someone out there knows the actual truth of this, I would be very interested in hearing it.
Now that we have our window and Direct3D objects to work with, all that remains is rendering frames in a loop and also rendering when we receive explicit
WM_ERASEBKGND messages. Under Vista we shouldn't receive too many of these messages due to the way the DWM works but we will still get them for situations such as window resizing.
Make sure you clear the back buffer to transparent before rendering each frame.
As the final action in our rendering function we use another new DirectX9 function
PresentEx. The only difference between
PresentEx and the old
Present, in the context of our needs, is the requirement to pass another
NULL's instead of four.
g_pD3DDevice->PresentEx(NULL, NULL, NULL, NULL, NULL);
Points of Interest
So what if you don't want the glass panel? What if you just want to seemingly render straight to the screen? Consider the Bubbles screensaver shipped with Vista. Of course it's possible! If you remove the
WM_SIZEBOX style flag from the window class, something rather cool happens. You have now effectively told the DWM that there is no non-client area that it needs to draw and so instead it just composits your client area's contents directly, alpha channel and all. The window itself seems to disappear (visually) and you are left with a perfectly composited 3D spinning cube. Make the window cover the entire desktop and render 50 of these cubes bouncing about and you've got your own Bubbles style screensaver. Ok, so in reality the Bubbles screensaver does snapshot the desktop first (for compatibility with machines running Vista minus desktop compositing and no doubt as a so called 'security consideration') but the principle is the same.
Just incase you are reading this article thinking "Wow! Full DirectX access means we can do our own window pixel shading a la Aero Glass" then sorry, but you are going to be disappointed. One thing that this technique will not allow you to do is create your own Glass type pixel shaders for your windows. To be more precise, you won't be able to do it in any way that is realistic performance-wise. It has already been explained by Microsoft developers that reading from or writing to the "screen" under the new DWM is an unbelievably expensive operation (it would require a forced composition of the entire workspace each time after all) and should be avoided completely. Just keep in mind that your application has no awareness of what is underneath it, only the DWM composition engine does. As long as you don't need to know what's under your window then the sky is the limit.
There you have it. You can develop efficient applications under Vista without .NET and managed languages that will cooperate completely with the DWM without the need of any dangerous hacks. Not only is this good news for C/C++ developers but also for old school assembly language developers. You can still write pure Win32 ASM apps and get all the new goodies to boot.
I will leave application of the information provided here to your no doubt very capable imaginations but keep in mind a couple of things as you dream. Just because you can do something doesn't mean you should. Yes, with this information you could go off and write an application that did the equivalent of running a Half Life 2 recorded demo as your desktop background but it wouldn't be very intelligent. Just keep in mind that you are now sharing both the CPU and the 3D capabilities of your video card with the operating system GUI on an intimate level. Not only that, but for the first time in Window's history the "desktop" is not created equal across all computers running Windows. Now the desktop environment is tied to hardware capabilities in the same way 3D games are. Your machine may be able to crunch 10 billion pixel shaded polygons a second and still leave the DWM room to breathe but Joe Blogs down the road may be barely capable of running the Aero interface.
The example application was developed in Visual Studio 2005 on Vista 64. The hardware included an AthlonFX62 dual core processor, ATI Radeon X1900XTX video card, and 2GB of RAM. I successfully compiled this code to both 32bit and 64bit binaries which I have included along with the source. However, I would strongly suggest that you setup a project using the source and build this yourself. You will need the latest Windows SDK, the latest DirectX SDK, and of course Windows Vista with Aero enabled. I have no idea how this will run on less capable systems but I would be very interested to know.
I am not stopping here, this is just the start, but I felt I should get this info out into the public domain as soon as possible to allow others to start playing with Direct3D in a whole new context.