Tuesday, March 30, 2010

Towards a D3D Windows Framework - 1

I haven't any progress on the D3D programming for two weeks. During this time, I'm thinking about building a windows framework based on D3D which will enable me to build real 3D applications more easily.

I'm following the book DirectX9 User Interfaces Design and Implementation, it is a  very good reference.

But before building the windowing system, I spent several days writing a very simple debug library that enables me to trace the debug information in a file, with a formate that I like.

Also, the book used very primitive techniques, I need to think about the feasibility using advanced libraries such as STL, boost to build the windowing system.

Tuesday, March 16, 2010

A Reallife Story about Memory Leak

My colleague alerted me today a potential memory leak in a component that I have written. It looks like the following piece of code:
try
{
    // new an object.
    // Call the Init() of the object, that may throw in case of error.
    // Put the object pointer into the pool.
}
CATCH...

I use capital CATCH here because actually, I used a macro that wraps the standard catch into one line. While it simplifies the code, it also made me to forget to do necessary cleanups in the catch clause. Finnaly, I gave up using the beautiful macro and went back to the standard catch and put a delete there.

More words on the pointer in the pool. It seems that to put the object itself in the pool is better but this is possible only when the class provides a default constructor and a copy constructor, if one uses a STL container as the pool. It is not my case, the class I'm using does not have a default constructor nor a copy constructor... Bad design or are there some special reasons to not provide a default or copy constructor?

Monday, March 15, 2010

A Generic Way to Render on GDI

In this post I'll introduce a generic way to do a DirectX rendering on Windows GDI.


In the previous post, I did an experiment with the texture created by calling the D3DXCreateTextureFromFile function from a file then obtain the level 0 surface of the texture and then get the DC of the surface, and finally, render this content in the DC to a screen DC. This is working because D3DXCreateTextureFromFile uses D3DPOOL_MANAGED by default, so it is possible to get the DC from its surface.


The key idea is then to create then render on a surface from which we can get its DC.


Notice it is not alway possible to get a DC from a surface, as specified by the DirectX document, the following restrictions apply when calling the GetDC method of a surface:


# IDirect3DSurface9::GetDC is valid on the following formats only: D3DFMT_R5G6B5, D3DFMT_X1R5G5B5, D3DFMT_R8G8B8, and D3DFMT_X8R8G8B8. Formats that contain Alpha are not supported because the GDI implementations don't have a well-defined behavior on the alpha channel. For more information about formats, see D3DFORMAT.

# Only one device context per surface can be returned at a time.

# IDirect3DSurface9::GetDC will fail if the surface is already locked. If the surface is a member of a mipmap or cubemap, IDirect3DSurface9::GetDC fails if any other mipmap or cubemap member is locked.

# IDirect3DSurface9::GetDC fails on render targets unless they were created lockable (or, in the case of back buffers, with the D3DPRESENTFLAG_LOCKABLE_BACKBUFFER flag).

# For surfaces not created with IDirect3DDevice9::CreateOffscreenPlainSurface, IDirect3DSurface9::GetDC will fail on default pool (D3DPOOL_DEFAULT) surfaces unless they are dynamic (D3DUSAGE_DYNAMIC) or are lockable render targets.

# IDirect3DSurface9::GetDC will fail on D3DPOOL_SCRATCH surfaces.

# IDirect3DSurface9::GetDC causes an implicit lock; do not retain the device context for later use. Call IDirect3DSurface9::ReleaseDC to release it.
It is valid to call IDirect3DSurface9::GetDC/IDirect3DSurface9::ReleaseDC on levels of a mipmap or cubemap, however, these calls will be slow to all miplevels except the topmost level, and GDI operations to these miplevels will not be accelerated.


As a result, GetDC of a default BackBuffer will usually fail as the default BackBuffer uses D3DPOOL_DEFAULT. So I need to create a lockable surface with CreateRenderTarget method:


HRESULT CreateRenderTarget(
  UINT Width,
  UINT Height,
  D3DFORMAT Format,
  D3DMULTISAMPLE_TYPE MultiSample,
  DWORD MultisampleQuality,
  BOOL Lockable,
  IDirect3DSurface9** ppSurface,
  HANDLE* pSharedHandle
);

and specify Locable as true. In order to fill the first five parameters, I can get the default BackBuffer's description and use the same information for the new surface.


The following steps are straightforward, when doing the render, first set the render target as the new surface, then render. Finally, instead of calling Present, get the DC of the surface then use GDI to put the contents on the screen.


Till now, I used a surface without an alpha channel, if you want to use an alpha channel in the surface and you are on a Windows XP as me, be sure to get the SP3 which contains an important fix KB 937106 that allows GetDC from a surface with an alpha channel.

Sunday, March 7, 2010

A Simple Example for Rending Texture with GDI

In this example, I'll demonstrate how to render a texture with GDI. You may wonder why I need to do that anyway as texture is generally used to render shapes in DirectX. The idea here is if I use the layered window with a transparent key color, the color in the texture, when finally rendered using GDI, will be transparent so I see everything behind my program window. Using only DirectX will not produce the same effect.

Let's start with a quick introduction to the layered window. Layed window is created by calling CreateWindowEx with label WS_EX_LAYERED. After the window is created, I can use either SetLayeredWindowAttributes or UpdateLayeredWindow to actually show the window. By calling SetLayeredWindowAttributes, I need to process the rending in the WM_PAINT message. As I'm going to use GDI mixed with DirectX, I want to have total control on the rending process, I'll use the UpdateLayeredWindow instead. This function saves me from the WM_PAINT message. I can call it whenever I finished the drawing.


Next I'll do the DirectX initialization, where I'll initialize the divece and especially, I'll create the texture. For simplicity, I create the texture from a bmp file using D3DXCreateTextureFromFile. The bmp file I'm using contains in the part where I want it transparent a special color, e.g. RGB(255, 128, 64). I'll specify the same color as the color key when calling the UpdateLayeredWindow.

After the texture is created, I'll retrieve the level 0 surface associated to it. This is done by simply calling the texture's GetSurfaceLevel method.

As I'm going to render the texture with GDI, I need two DC handles to call the UpdateLayeredWindow, one is the source and the other is the destination. I already have a surface ready to be presented on the screen, I need to get the DC associated with it. This is done by calling the surface's GetDC method. If the call succeeds, the source is ready. Then I still need to prepare the destination. That is simply a call to global GetDC.

There are still some parameters to prepare in order to call UpdateLayeredWindow. The BLENDFUNCTION sturcture contains information about the blend I want to do with the contents to be rendered. I give the following values:
blend.BlendOp = AC_SRC_OVER;
blend.BlendFlags = 0;
blend.AlphaFormat = 0;
blend.SourceConstantAlpha = 255;

I need to specify also the new position, size of the new window. Be careful here, I need to retrieve the size of the bmp file I have loaded as it will be my entire window. What's more, give a size larger than the actual size will not work. Currently, I load the bitmap and get the size information from it, I'm not aware the way to get this information from the texture which is created from the bmp also. So I actually load twice the bmp file.

Next, the location of the layer in the source DC, which is usually (0,0).

Finally, I set the transparent color as the color key and the ULW_COLORKEY as the flag and call the UpdateLayeredWindow.

The whole process is:
  • Create a layered window.
  • Load bmp into texture.
  • Get surface 0 of the texture.
  • Get DC of the surface.
  • Get DC of the destination (Desktop)
  • UpdateLayeredWindow with source DC as the DC of the surface, destination DC as the Desktop.

Something I need to note, with D3DXCreateTextureFromFile, I can always get the DC of the surface 0 associated. But with a texture created by CreateTexture, I can fail sometimes when get the DC. This is due to the usage and memory pool used by the methods. D3DXCreateTextureFromFile always uses D3DPOOL_MANAGED. (Cited from DirectX Doc for this method: "Note that a resource created with this function will be placed in the memory class denoted by D3DPOOL_MANAGED.) And the GetDC method of the surface has several limitations and especially, it will fail on D3DPOOL_DEFAULT unless the usage is dynamic (D3DUSAGE_DYNAMIC). 

I'll investigate more on this issue. But the idea is clear, once I have surface from which I can get its device context, I'll be able to render it to a layered window. On the other side, I need also be able to render on the surface, obviously. So I'll need a surface that I can render on it and can get DC from it. I need to see if this is actually possible, otherwise, maybe I can render on on surface and put the result to another one from which I can get the DC.

Friday, March 5, 2010

Mixing D3D Rendering and GDI

I tried my first solution to render a 3d scene to transparent window,
that is, get the desktop bitmap with GDI then render 3d on it. The
performance is bad.

So I tried the second solution, which is more or less a common
practice, to render on a texture then get it's surface then get the
device context of the surface. This DC can then be used for GDI
rending. To make transparent effect, just use a layered window with a
transparent color key and use the same color for the transparent part
in the texture. Also I need to use UpdateLayeredWindow onstead of
standard GDI rendering in WM_PAINT.

There are some tech details I need to solve in order to use it freely
with different types of textures.

Thursday, March 4, 2010

Email to blogger, give it a try!

Maybe i can write on the bus. :)
Just a small problem, no way to give the post a label?

Wednesday, March 3, 2010

Vim tips

Vim is fantastic. It may improve your editing efficiency a lot. After using it for four years, I discovered recently something really useful.

You can do an "instant search" with * (for forward) or # (for backward) command when your cursor is on a word. These commands saves me two seconds for a search with my word under the eyes. Before I switch search mode and type the word. These commands match whole word only, but you can use g* or g# to do a partial match.

Also I started to try to locate my cursor precisely faster with commands each time I want to move it. Before I use l,i,j,k,home,end... I need to repeat many times before the cursor arrives at the good position, now I'm using commands like e,E,b,B,A,0,$,w,W... I need some time to get used with them.

When using visual mode to select lines, I can use the v$ command that will select to the end of the line regardless where my cursor currently is, I use j,k to move upwards or downwards.

Now I also learned to use the J or gJ command to join multiple lines into one. This is extremely useful because I need to do this occasionally and it is really boring to do it manually.