Direct3D rendering to desktop

Started by
6 comments, last by xtremeqg 17 years, 5 months ago
I've been attempting to have Direct3D render to the desktop background instead of a window / fullscreen for a few days now, to no avail. Digging extensively through numerous code snippets, forums, reference documents and so on I couldn't find this information anywhere. My investigation first led me to the parameters of IDirect3D9->CreateDevice on which there are two arguments of interest: BehaviorFlags and pPresentationParameters. BehaviorFlags is actually a list of #define's (D3DCREATE_ADAPTERGROUP_DEVICE, D3DCREATE_DISABLE_DRIVER_MANAGEMENT, D3DCREATE_DISABLE_DRIVER_MANAGEMENT_EX, D3DCREATE_FPU_PRESERVE, D3DCREATE_HARDWARE_VERTEXPROCESSING, D3DCREATE_MIXED_VERTEXPROCESSING, D3DCREATE_MULTITHREADED, D3DCREATE_NOWINDOWCHANGES, D3DCREATE_PUREDEVICE, D3DCREATE_SCREENSAVER, D3DCREATE_SOFTWARE_VERTEXPROCESSING) of which none have anything to do with rendering to a background. D3DPRESENT_PARAMETERS is a struct, of which two values are of interest: SwapEffect and Flags. The D3DSWAPEFFECT enum (D3DSWAPEFFECT_DISCARD, D3DSWAPEFFECT_FLIP, D3DSWAPEFFECT_COPY) only configures how the swap chain will operate. Flags (D3DPRESENTFLAG) is another list of defines (D3DPRESENTFLAG_DEVICECLIP, D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL, D3DPRESENTFLAG_LOCKABLE_BACKBUFFER, D3DPRESENTFLAG_VIDEO) which again only deal with the swap chain. So much for this function. My second hint was IDirect3DDevice9->SetRenderState which has a very large amount of flags and values. The following were of interest: D3DRS_ALPHATESTENABLE, D3DRS_SRCBLEND, D3DRS_DESTBLEND, D3DRS_ALPHAREF, D3DRS_ALPHAFUNC, D3DRS_ALPHABLENDENABLE, D3DRS_BLENDOP, D3DRS_SRCBLENDALPHA, D3DRS_DESTBLENDALPHA and D3DRS_BLENDOPALPHA. After some testing I found out they purely control the actual drawing of geometry / textures. I cannot remember where the original post was or who was the original poster, but someone suggested using GetDesktopWindow() to get the desktop's HWND. This does give you the HWND, however using this in either D3DPRESENT_PARAMETERS->hDeviceWindow and/or IDirect3D9->CreateDevice does nothing whatsoever (I remember something vague about not being owner of the surface and therefore not being able to use it). My final option was IDirect3DDevice9->Present. It has one interesting parameter: hDestWindowOverride. Setting this to the value returned by GetDesktopWindow()still had no effect whatsoever. Since I used DirectDraw7 in the past, I know it is possible to draw to the desktop by setting it to a single color and using DirectDraw's colorkeying ability. Considering I am already using DirectX9 (which, as far as I know is being used by DirectDraw) I didnt really want to invoke yet another clunky COM object but I seem to have no choice. MSDN suggests using DirectDrawCreateEx and then IDirectDraw7->QueryInterface to create a Direct3D object. However upon attempting this with IID_IDirect3D9 as parameter, it only returned E_NOINTERFACE. DirectDraw7 must be deprecated further than expected. Not a very big deal, I could create an instance of each using the standard way. The question remains however, how to connect DirectDraw and Direct3D? Using a IDirectDrawSurface7 as a render target perhaps? It seems not at all compatible to a IDirect3DSurface9. (Havent tried it yet) I suppose manually Blt'ing (if that is even possible) could be done by Lock'ing both surfaces but Im pretty sure that would severely impact performance. Furthermore this approach would mean converting to YUY and/or YV12 due to overlay limitations. It would also disable any other program using overlays as most cards only support one hardware overlay. A final problem I have encountered on ATI cards set at a resolution above 1024x768 is that the resulting image is blurred. So far Ive found no information whatsoever regarding issue. Any ideas?
Advertisement
The present operation is doing nothing because the window that you've given it is occluded by child windows.

Run Spy++ and use the Find Window feature (Alt+F3) and drag the finder icon over the desktop to get the window handle. As a test, use that window handle value in your app and see if Present works as you expect it would.

I don't know what you're trying to do, but realize if the desktop repaints it will clobber whatever you've drawn using this method. You might want to create your own window and make it transparent (WM_POPUP and then eat the WM_ERASEBKGND message) and get D3D to use it instead of the desktop.

Quote:Original post by don
The present operation is doing nothing because the window that you've given it is occluded by child windows.

Run Spy++ and use the Find Window feature (Alt+F3) and drag the finder icon over the desktop to get the window handle. As a test, use that window handle value in your app and see if Present works as you expect it would.


Lol, I'm pretty sure I left (atleast a portion) of my desktop visible. Or are you implying that the desktop has an x number of child windows I am not aware of? I suppose I could call EnumChildWindows on the desktop window handle to see if this is the case.

Quote:Original post by don
I don't know what you're trying to do, but realize if the desktop repaints it will clobber whatever you've drawn using this method. You might want to create your own window and make it transparent (WM_POPUP and then eat the WM_ERASEBKGND message) and get D3D to use it instead of the desktop.


What I am trying to do is:
Render a 3D scene/image/object using Direct3D9 and display the result on the desktop background, below all other windows, the taskbar, icons etc.

True, repaint indeed would clobber it. But Im not even seeing as little as a flicker. A user-generated window will afaik not sit below icons. You can have it sit below other windows and force it to stay there using Z-order modification but it will not put it below icons, which is what I am trying to do. Or did you mean to put it above everything else? That might be an option, I haven't tried it yet. Would result in having a visible (taskbar) application window... :(
Realize that there could be a window stretched across your entire desktop (spanning multiple monitors) that is transparent, but still affects the clipping region. Any drawing to a window underneath the transparent window would still be clipped by GDI.

Spy++ is your friend when snooping through window heirarchies. Use it.
// find Program Manager
HWND hWnd = FindWindowEx(GetDesktopWindow() , 0 , "Progman" , "Program Manager");
// find SHELLDLL_DefView
hWnd = FindWindowEx(hWnd , 0 , "SHELLDLL_DefView" , 0);
// find Desktop Folder!!
hWnd = FindWindowEx(hWnd , 0 , "SysListView32" , "FolderView");
// initd3d by desktop forlder window!!
// You must use this hWnd to create D3DDevice when rendering to desktop !!!
InitD3D(hWnd );
That worked like a charm mybios! Thanks. Altho it isnt drawing _under_ the icons (it seems to be using the same surface) it does render under every other window. I'm pretty sure I can manage tho, finding a surface in the hierachy that does work for what I have intended. And in the worst case catching WM_PAINT messages to manually re-create them. I will have to eat the WM_PAINT messages anyway as it is currently causing glitches like don pointed out before. Thanks!

An unnoticed issue for most users has to do with multimonitor support. The default "use the entire surface" (using Present with NULL) will cause it to blit the entire image accross all my screens in a stretched manner. Amusing (also causing some excessive glitches due to exceeding my card's capabilities) but not desired :) Simple case of enumerating all display adapters and figuring out which RECT to use shouldn't be too difficult. Ive temporarily hardcoded it to suit my system as a test and it blits to the right monitor just fine.

There is a noticeable performance impact (cpu usage increase by 20-30% as opposed to rendering to my own window. I am unsure which is causing this but I am guessing it has to do because I used D3DSWAPEFFECT_COPY (required because Im not using the entire surface) instead of D3DSWAPEFFECT_DISCARD.
I doubt that the perf hit is due to COPY vs. DISCARD. When you're running in Windowed mode, DISCARD is performing a copy so it should be a wash. Simple to try and verify though.

What might be having an impact is all of the clipping (i.e. multiple copy operations per Present) due to the overlapping windows and whatnot on the screen.
Hrm... perhaps... I'll test some more... I'm not sure its because of clipping operations. It doesnt really seem to matter whether or not I have other programs obscuring part of the desktop or not.

This topic is closed to new replies.

Advertisement