[2D] I've written a wrapper for DirectX9 to do 2D stuff

Started by
2 comments, last by Evil Steve 14 years, 1 month ago
I wanted to update my 2D Windows games (which have only used GDI up until now) so I've written a single C file that takes care of all the complex DirectX issues and lets me just call simple functions to load images, create offscreen buffers, BLT 2D sprites, draw text, draw lines etc... I was worried that some Windows users might not have DirectX 9 installed (or have a hardware accelerated video card at all) so each of the functions is replicated using GDI+ so at least the user will see something, albeit much slower. It also tries to load any D3DX9_nn.dll it can find so your game won't be dependent on a single version of that DLL. For my 2D games (mostly board game style games) blazing speed isn't essential but it'll be nice to at last be able to use PNGs and have anti-aliased text! I've had complaints that my games have got slower when users update to Vista and lose GDI hardware acceleration so it should solve that problem too. I'm happy to share the code which is at: http://www.windowsgames.co.uk/blitz2D.html and any comments/criticisms would be greatly appreciated!
Sean O'Connorhttp://www.windowsgames.co.uk
Advertisement
Looks quite nice - although I have some comments:

1. I wouldn't try to load D3DX DLLs from the future; what if any of the function signatures you need change in upcoming versions of the DLL (Which has happened before)?

2. You never check if CreateVertexBuffer() succeeds. It's unlikely to fail, but if it does, you'll get a crash later on.

3. Your vertex buffer is in D3DPOOL_DEFAULT, but has a usage of 0, which implies it's static. If it's static, then it should be in D3DPOOL_MANAGED, and if it's dynamic, it should have D3DUSAGE_DYNAMIC. Related to that, DX_DrawImage Lock()s the vertex buffer - if you call that more than once per frame with a static buffer, you may have some severe performance issues (The debug runtimes will also scream at you)

4. I'd be inclined to use ID3DXSprite for drawing images instead of a vertex buffer; it'll be more efficient when drawing multiple images, and is less code to worry about.

5. An option to toggle fullscreen on/off and to start up in fullscreen mode would be nice.

6. You don't handle a lost device - if the user changes resolution, your app will stop rendering until it's closed.

7. DX_Resize() doesn't resize the backbuffer, which means you'll get stretched graphics.

8. In DX_CreateOffscreenSurface, you call wsprintf, but szMessage is a char buffer, not wchar_t. The buffer is also too small, so you have a buffer overflow ("Not enough video memory to create surface of size width=%d height=%d" is 69 characters, your buffer is 64).

9. I only glanced over the code, but why does DX_CreateOffscreenSurface create a render target?
Wow, thanks Steve! I wasn't sure if I'd get a reply at all to this. To be honest I'm very new to DirectX and your advice is invaluable!

Quote:
1. I wouldn't try to load D3DX DLLs from the future; what if any of the function signatures you need change in upcoming versions of the DLL (Which has happened before)?

OK, I hadn't realised that could be a problem so I'll only try to load 42 and earlier, and then update it as time goes by.
Quote:
2. You never check if CreateVertexBuffer() succeeds. It's unlikely to fail, but if it does, you'll get a crash later on.

OK, thanks, I'll add a check for that.
Quote:
3. Your vertex buffer is in D3DPOOL_DEFAULT, but has a usage of 0, which implies it's static. If it's static, then it should be in D3DPOOL_MANAGED, and if it's dynamic, it should have D3DUSAGE_DYNAMIC. Related to that, DX_DrawImage Lock()s the vertex buffer - if you call that more than once per frame with a static buffer, you may have some severe performance issues (The debug runtimes will also scream at you)
4. I'd be inclined to use ID3DXSprite for drawing images instead of a vertex buffer; it'll be more efficient when drawing multiple images, and is less code to worry about.

I'll take a look at ID3DXSprite but I thought there were some issues from this article: http://www.gamedev.net/reference/articles/article1608.asp that scared me off using it.
I've read that Locking is slow, so I'll look into what I can do about that.
Quote:
5. An option to toggle fullscreen on/off and to start up in fullscreen mode would be nice.

I'll have a go at adding that. Personally I like my games in a Window but I can see why people might want to go full screen.
Quote:
6. You don't handle a lost device - if the user changes resolution, your app will stop rendering until it's closed.

Ahhh, that's what a lost device is! I'd read about losing devices but never figured out how a device could become lost. I don't think there's any code in "Beginning DirectX 9" so I'll do some Googling.
Quote:
7. DX_Resize() doesn't resize the backbuffer, which means you'll get stretched graphics.

What I do is create a back buffer as big as the screen resolution at start up so it doesn't need to be resized when you resize the window. Originally I was resizing the back buffer to be as big as the client area on a resize but I think all of the textures need to be reloaded each time that happens?
Quote:
8. In DX_CreateOffscreenSurface, you call wsprintf, but szMessage is a char buffer, not wchar_t. The buffer is also too small, so you have a buffer overflow ("Not enough video memory to create surface of size width=%d height=%d" is 69 characters, your buffer is 64).

wsprintf is just the Windows version of sprintf - according to His Holiness Charles Petzold. I'm not sure what the wide version of that is and I think that's what you're confusing it with? Thanks for spotting the buffer overflow. How did you spot that!!!?
Quote:
9. I only glanced over the code, but why does DX_CreateOffscreenSurface create a render target?

I thought I needed a render target so that later on I could call SetRenderTarget() and draw to that surface?

Sean O'Connorhttp://www.windowsgames.co.uk
Quote:Original post by SeanPTOConnor
I'll take a look at ID3DXSprite but I thought there were some issues from this article: http://www.gamedev.net/reference/articles/article1608.asp that scared me off using it.
That's a very old article (8 years old). ID3DXSprite is much better now. Internally, it uses a dynamic vertex buffer and a static index buffer, and it doesn't do anything particularly heavyweight.

Quote:Original post by SeanPTOConnor
Ahhh, that's what a lost device is! I'd read about losing devices but never figured out how a device could become lost. I don't think there's any code in "Beginning DirectX 9" so I'll do some Googling.
If you do any fullscreen stuff, you'll get a lost device if you alt+tab. The docs have a section that's worth having a quick read over.

Quote:Original post by SeanPTOConnor
What I do is create a back buffer as big as the screen resolution at start up so it doesn't need to be resized when you resize the window. Originally I was resizing the back buffer to be as big as the client area on a resize but I think all of the textures need to be reloaded each time that happens?
I see. If you resize the backbuffer, then you need to Reset() the device to apply that, so the same rules apply as with a lost device - all default pool resources need released and re-created. D3DXCreateTextureFromFile will create the textures in the managed pool, so they don't need released, they'll survive the reset just fine.
It seems a little wasteful to me - if I have a 1600x1200 resolution but am making a 320x200 "oldschool" game, there's a lot of wasted space.

Quote:Original post by SeanPTOConnor
wsprintf is just the Windows version of sprintf - according to His Holiness Charles Petzold. I'm not sure what the wide version of that is and I think that's what you're confusing it with? Thanks for spotting the buffer overflow. How did you spot that!!!?
Ah, that would be it - I'm thinking of swprintf. I noticed the overflow because the wsprintf() function caught my attention, and then I thought "64 characters is a bit small" and "That error looks quite large" [smile]

Quote:Original post by SeanPTOConnor
I thought I needed a render target so that later on I could call SetRenderTarget() and draw to that surface?
Ah, I see - If you want to render to that image surface then yes, you'll need it created as a render target. Although, if this is designed to be a simple to use library, I can't really think why you'd need to draw to another image surface other than the backbuffer? Still, it's a nice option to have I guess.

This topic is closed to new replies.

Advertisement