Thread: Direct2D on GDI Device Context

  1. #1
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446

    Direct2D on GDI Device Context

    I'm getting ready to overhaul my whole code that renders a map of of ~14k hexagons to use Direct2D instead of GDI. Screenshots here.

    I could never really get fully rid of screen flicker on the minimap as I initially thought I did. Scrolling the main map window also does present one problem: fast scrolling produces a ghost image. Meanwhile, although I do like the aliased look of the map window, I think it will benefit a lot more from antialias. Direct2D will also allow me to implement a zoom feature so the player can see more or less of the map according to their needs and visual requirements.

    So 3 years later and as I actually near the "completion" of this project, it's time to look back and start changing stuff.

    But I have a few questions:


    • If I render Direct2D to a GDI DC, will I still take advantage of hardware emulation?


    • No backport to Windows XP really p... me off. I'll have to leave the GDI code and somehow choose between them both at runtime. Suggestions?


    • What does it mean exactly Direct2D's ability to render to intermediate layers? Are we talking graphics layers as in Photoshop (which would be a real cool thing and much needed by me to create decals for the map), or is this something else entirely?


    And finally... do you have an opinion on Direct2D? Should I used Direct3D's own rendering_2d() function instead? Something else? Keep in mind the type of program I'm involved with.
    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  2. #2
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    If I render Direct2D to a GDI DC, will I still take advantage of hardware emulation?
    As far as I know, it will be accelerated as far as the final GDI copy operation if setup correctly.

    I'll have to leave the GDI code and somehow choose between them both at runtime. Suggestions?
    Sounds like a problem dynamic libraries was born to solve.

    Use the same library name, the same interface, link at compile time, provide both DLL and your users only need to choose which to keep.

    Should I used Direct3D's own rendering_2d() function instead?
    I would. Once you get used to it and code a few primitives, Direct3D is as easy to use for 2D as just about any other library.

    That said, if you are going to use Direct3D10 or Direct3D11 you'll have the same problem as above. If you'd be going that route, I'd suggest to stick with Direct2D that you seem to be comfortable with.

    Soma

  3. #3
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446
    Quote Originally Posted by phantomotap View Post
    As far as I know, it will be accelerated as far as the final GDI copy operation if setup correctly.
    Yeah. I meant acceleration, not emulation. Glad you caught that.

    Hmm... so I'd be better off if I write to a hWND render target instead.

    Sounds like a problem dynamic libraries was born to solve.

    Use the same library name, the same interface, link at compile time, provide both DLL and your users only need to choose which to keep.
    Ugh!
    But I guess. Was hoping for something simpler than moving whole sections of code into a library. Like a magic solution. But yeah.
    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  4. #4
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    A fair word of warning is that you may run into flickering and airspace issues when mixing the two. I have experienced this with mixing WPF and D3D as well as GDI and D3D.

  5. #5
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    flickering and airspace issues
    I get flickering. I still play games on the Atari 2600. I know flickering. What on R'lyeh are "airspace issues"?

    Soma

  6. #6
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Airspace issues are when Direct3D and GDI/WPF/Direct2D or what have you attempt to fight for the same DC. It's very close to what happens in Z-fighting when two vertices are so close in the zbuffer that they constantly fight back and forth which causes shimmering and flickering of the object. That is caused by round-off errors in the 32-bit zbuffer or the fact that two vertices are fighting for nearly the same z coordinate. What happens when GDI/WPF/Direct2D fight with Direct3D is that at times GDI/WPF/Direct2D win and at times Direct3D wins. You can re-create the problem quickly by rendering Direct3D into a window and then trying to draw via GDI calls to the same window.
    Last edited by VirtualAce; 07-16-2010 at 06:07 PM.

  7. #7
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446
    Well, I decided to completely ignore D2D and instead keep up with GDI. There's some irony in the fact Microsoft decision to not backport D2D is such a limitation to its adoption. I couldn't give a rat's arse if it couldn't be hardware accelerated there. But we do need the API. It would help tremendously exactly in these situations where we need to run on Windows XP. I'm not going to write two map rendering libraries (one of which can't even be ported to other systems) just because Microsoft decided to be such a tight arse towards Windows XP.

    Besides I do get a lot better GDI performance out of Windows 7, to the point that the flickering is all but entirely gone. It's Windows XP that is giving me the most problems. But, I suspect I still have room for improvement in my current code. So I'll be better off spending my time there, than introducing D2D which can't even be used in Windows XP.

    I'm however still pondering eventually adopting D3D's rendering_2d(). Right now it's starting to shape up as my best option. Should also introduce me to D3D, an area I know nothing about. But for now GDI it is.
    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  8. #8
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    O_o

    I've never heard the term.

    I've never had a problem with this, but I don't personally deal with any applications using WPF. With my targets it is only a matter of using the various API correctly. Is this problem related to programmer API abuse or is it a natural aspect of WPF?

    I've done some searching, looked at some snaps, and examined some code. From what I've seen this problem can be avoided by not being foolish. (The examples I've seen all interleave commands from multiple API without flushing rendering operations.) However, I've not found any WPF API to "help" with the issue. (I'm imagining an API to temporarily suspend the Direct3D rendering thread or propagation of a "mutex".)

    Do you have direct experience with this in production? (I'm not talking about a simple application designed to exhibit the problem.) Have you developed a protocol to prevent the problem? (The advice of "don't mix the two" I've found repeated so often is not interesting.) If you have, can you provide an explanation or a link to an explanation?

    Sorry for partially hijacking the thread Mario. I find "preventative maintenance" and "proactive development" a necessity.

    Soma

  9. #9
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446
    Quote Originally Posted by phantomotap View Post
    Sorry for partially hijacking the thread Mario. I find "preventative maintenance" and "proactive development" a necessity.
    Don't worry. It's alright really. The topic is interesting.
    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  10. #10
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Do you have direct experience with this in production? (I'm not talking about a simple application designed to exhibit the problem.) Have you developed a protocol to prevent the problem? (The advice of "don't mix the two" I've found repeated so often is not interesting.) If you have, can you provide an explanation or a link to an explanation?

    1. Yes I have experience this in a rather large and complex application that I helped design and implement. I really cannot get into the details though.

    2. The problem has been minimized but not solved. I've asked some game industry contacts about the problem but unfortunately none have offered any more help than what we have already tried or thought of.

    3. The simple and extremely annoying answer is don't mix the two but this is a fairly short-sighted view b/c often it is needed and useful to mix the two in windowed mode only. Mixing GDI/WPF, etc in full-screen mode is absolutely not a good idea. However windowed is perfectly plausible and at times necessary.

    4. I have asked on MSDN about the problem to no avail. The only explanation I can give is that I 'believe' two things are happening. I found some information about GDI draw calls being extremely slow (duh) but what I did not know is that they are so slow that some of them 'yield' cycles before completing the drawing. This means the system would go through a complete render/update loop and the GDI call would still not be complete. This means the GDI object in question would not draw correctly and could even steal away the DC from Direct3D which would wreak havoc on Windows XP ....and trust me...it does.

    The second issue is probably due to the events in Windows XP. Events can come in at any time and we suspect they are coming in during rendering. If those events cause the system to follow a code path that leads to messing with items that are currently being rendered...bad things will happen.

    So far the only answer is to mutex the rendering so it effectively linearizes the entire process. After all there really is no need to respond to events in the middle of a update/render cycle. This has minimized the flickering but not removed it completely. The issue is still being investigated.

    As for WPF the experience I had with it was that it's widgets would not display unless I forced a minimize on the Direct3D window which then caused a lost device condition. Once the WPF portion was complete it would then maximize the Direct3D window. Because the window is a full-screen window the video driver then takes over the entire display and the system continues on as long as the rendering system can correctly respond to and handle lost device and reset device conditions. This is actually pretty simple to do. I've had little luck mixing WPF and windowed D3D. One or the other or both do not display correctly. There are blurbs on the internet about using a new object in .NET that resolves the issue but it is not in the .NET version I'm targeting.

    I've had experience (more like self-inflicted pain) mixing the following pieces all at the same time on the same screen or on dual displays:

    • Direct3D and WPF (does not work unless you minimize Direct3D window). Several airspace issues can occur if you do not minimize the D3D window and often WPF widgets will fail to display or they will only partially display or display one frame and then not the next.
    • Direct3D and GDI - causes severe flickering to occur if the process is not linear. Flickering occurs not just within the application but throughout the entire desktop and is even worse on a dual monitor system since the second display is slower than the first anyways.
    • Direct3D, GDI, and WPF - severe airspace issues and flickering. GDI and WPF get along about as well as Direct3D and GDI. This can be done but I'm not at the point where I know exactly how to pull it off yet.



    There are sites out there that talk about grabbing Direct3D's DC and then using that to draw the GDI. Unfortunately in mixed language projects it is not always possible nor plausible to pass a DC across a native/managed boundary. As well since both are drawing to the same HWND one would think they are already using the same DC. If they were not using the same DC I have no idea how one or the other would ever display at the same time. The internet has not been of much help with this issue. Most spout off a day 1 graphics and GDI answer like 'use double buffering' when they really do not understand the problem at hand. When I find a resolution to the issue I'll be sure and share the information since it is quite valuable.

    I have not worked with Direct2D but I suspect it will have the same issues since it uses an IDirect3DDevice<X> under the hood. I have not tried to mix XNA and Direct3D in the same app but I suspect that it would fail miserably.
    Last edited by VirtualAce; 07-17-2010 at 01:26 AM.

  11. #11
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    This means the system would go through a complete render/update loop and the GDI call would still not be complete.
    This is where carefully using the "DC" obtained from Direct3D comes into play. (I'll talk more about this in a second.)



    Events can come in at any time and we suspect they are coming in during rendering.
    This is very likely, but I do not believe it to be the primary concern as the event system is certainly not causing the problem in all cases.



    There are sites out there that talk about grabbing Direct3D's DC and then using that to draw the GDI.
    This is actually the correct approach for interleaving GDI and Direct3D commands. Application source following the below protocol has never exhibited this problem as far as I know. (It may have simply never been reported back to me.)

    Code:
    1) Obtain the Direct3D "DC" from the relevant surface.
    2) Initialize and install any GDI objects necessary to perform one set of operations.
    3) Issue GDI commands using only simple GDI primitives.*
    4) Explicitly flush the potentially queued commands.
    5) Reinstate any GDI objects that must be returned to the surface default.**
    6) Free any newly allocated GDI objects back to the system.
    7) Return the "DC" to the relevant Direct3D surface.
    
    *(The functions that perform one logical operation and return a "BOOL" value.)
    **(I've been told that this is not necessary. My experience from older systems says that it is a necessity.)
    Once you start issuing commands using the GDI primitives you should not initialize any new objects or reinstate any old objects until after the commands are flushed.

    Once you flush the commands you should not issue any new commands.

    This means, for example, that you should redo the entire process if you want to use the GDI to draw text in two different colours.

    It can be a slow process, but it is faster than any other form of controlled sharing I've attempted.



    So far the only answer is to mutex the rendering so it effectively linearizes the entire process.
    Unfortunately in mixed language projects it is not always possible nor plausible to pass a DC across a native/managed boundary.
    I can't personally speak on this, but I will pass along that the official "rendering API interop" protocol, according to a trusted associate, does exactly this behind the scenes.



    Most spout off a day 1 graphics and GDI answer like 'use double buffering' when they really do not understand the problem at hand.
    Don't feel bad. That is the same nonsense I keep turning up. That and "don't do it".



    This can be done but I'm not at the point where I know exactly how to pull it off yet.
    I find it hard to believe that there isn't a simple method to "get at" the underlying surface WPF uses. I guess that is just Microsoft being stingy. How many years did they tell programmers "don't draw on the NC area" before finally releasing a full description of how to do it properly?



    When I find a resolution to the issue I'll be sure and share the information since it is quite valuable.
    Ditto.

    Soma
    Last edited by phantomotap; 07-17-2010 at 02:50 AM. Reason: none of your business

  12. #12
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    I can't personally speak on this, but I will pass along that the official "rendering API interop" protocol, according to a trusted associate, does exactly this behind the scenes.
    Well we were thinking that if a DC is just a pointer (IE: a handle) in C++ then passing it to managed should be as simple as passing it as an IntPtr. This is what I am currently investigating. If you have been successful at this this gives me hope that I can be too b/c, contrary to other forums/sites, it is extremel useful to be able to combine rendering and GDI on the same DC. For those that cannot see the benefit of that it probably wouldn't do much good for me to explain it to them either so I normally just give these folks a wide berth.

    Again we are talking windowed D3D here. I think we all realize that using GDI dialogs and so forth in full-screen is not a good idea. The problem I'm having is not with dialogs or any of that but moreso with graphical calls like LineTo() and so forth that draw directly to the DC of a window that is also being rendered to by Direct3D.


    1) Obtain the Direct3D "DC" from the relevant surface.
    2) Initialize and install any GDI objects necessary to perform one set of operations.
    3) Issue GDI commands using only simple GDI primitives.*
    4) Explicitly flush the potentially queued commands.
    5) Reinstate any GDI objects that must be returned to the surface default.**
    6) Free any newly allocated GDI objects back to the system.
    7) Return the "DC" to the relevant Direct3D surface.
    I cannot do number 5 for sure and number 3 is a bit vague. Since calls are being made from C# I reckon that every entity using Graphics to draw is using GDI under the hood. This is why number 3 is a bit vague. Exactly what qualifies as a 'simple' primitive in C# Graphics calls? Number 5 cannot be performed since even if I tried to force the object to go out of scope, and I have, it stays in scope until C# is good and ready to clean it up.

    I think at this point my associates and I are past the point of common internet help on this which is why I've been asking some game industry contacts b/c I'm sure at least one of them has encountered this. Since most of the apps that would run into this are most likely internal tools it is quite possible they do not address it or fix it and just live with it or somehow minimize its impact.
    I would Google some more for it but if I find one more site that comes up with the amateurish answer of use double buffering or just don't do it I'll probably scream. You wouldn't want me to scream now would you? It's definitely not a pretty thing to see a grown man scream.
    Last edited by VirtualAce; 07-17-2010 at 10:45 AM.

  13. #13
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    I've tried to search a little more. I've had no luck.

    I've talked to a few more people. The responses I've gotten were more helpful, but still not really useful. I've mostly gotten "use resource locking" or "don't use managed code", which we already knew, but at least it is something. I have heard more on some mythical "temporarily disable the WPF rendering thread Direct3D widget" beast that is a native of the Windows implementation of ".net4", but even this is probably the same thing you already knew about.



    I cannot do number 5 [...] ready to clean it up.
    I can't speak for ".net" managed environments. I posted that thinking it may help you find a solution for those environments.

    Since most of the apps that would run into this are most likely internal tools it is quite possible they do not address it or fix it and just live with it or somehow minimize its impact.
    *shrug*

    I guess that could be the case. I've seen some almighty bugs in code that wasn't supposed to "see the light of day".

    Soma

  14. #14
    Registered User
    Join Date
    Jan 2011
    Location
    Venezuela
    Posts
    6

    SDL?

    I would seriously consider using SDL instead. Using plain GDI for a game is a horrible experience both from the programmer's point of view and the user's point of view. Microsoft created WinG in the old days to tackle that issue and then switched to DirectDraw. Then we got GDI+ and Direct2D, but keep in mind that if you code in SDL, you can port effortlessly to other platforms and the code is pretty clean in general.

    I would advise against using Windows scrollbars to move around, it's so much better to use RTS games approach, which is, active zones at the borders for scrolling, arrow keys and minimap. Since you have a minimap you have done the hardest part already .

    If SDL is not an option, consider using either Direct2D or if you want to support XP, then use DirectDraw. All of them are hardware accelerated if possible, and even if DirectDraw is deprecated, you can still use it.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Device & Rendering Context
    By hannibar in forum Windows Programming
    Replies: 1
    Last Post: 12-16-2005, 05:28 AM
  2. Device context woes
    By samGwilliam in forum Windows Programming
    Replies: 13
    Last Post: 04-16-2005, 10:01 PM
  3. Replies: 4
    Last Post: 06-30-2004, 03:11 PM
  4. Device Context
    By gvector1 in forum Windows Programming
    Replies: 2
    Last Post: 03-25-2003, 08:38 AM
  5. Replies: 1
    Last Post: 05-09-2002, 07:14 AM