lundi 13 décembre 2010

API independent primitive topology

Here is a simple uber function for retrieving the primitive topology in a native API format from an API independent format:

enum PrimitiveTopology
{
 PT_POINTS = 0,
 PT_LINES,
 PT_LINE_STRIP,
 PT_TRIANGLES,
 PT_TRIANGLE_STRIP,
 PT_TRIANGLE_FAN,
 PT_QUADS,
 PT_QUAD_STRIP
};

enum GraphicsAPI
{
 DirectX11,
 DirectX10,
 DirectX10_1,
 DirectX9,
 OpenGL,
 OpenGLES
};

template <GraphicsAPI API>
int getPrimitiveTopology(PrimitiveTopology pt)
{
 switch (API)
 {
  case DirectX11:
  case DirectX10:
  case DirectX10_1:
   switch(pt)
   {
    case PT_POINTS:
     return D3D_PRIMITIVE_TOPOLOGY_POINTLIST;
     break;
     
    case PT_LINES:
     return D3D_PRIMITIVE_TOPOLOGY_LINELIST;
     break;
     
    case PT_LINE_STRIP:
     return D3D_PRIMITIVE_TOPOLOGY_LINESTRIP;
     break;
     
    case PT_TRIANGLES:
     return D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
     break;
     
    case PT_TRIANGLE_STRIP:
     return D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
     break;
     
    case PT_TRIANGLE_FAN:
     return D3D_PRIMITIVE_TOPOLOGY_UNDEFINED;
     break;
     
    default:
     assert(0 && "getPrimitiveTopology::DirectX10+ Unknown or unsupported primitive topology !");
     return -1;
     break;
   }
   break;
  case DirectX9:
   switch(pt)
   {
    case PT_POINTS:
     return D3DPT_POINTLIST;
     break;
     
    case PT_LINES:
     return D3DPT_LINELIST;
     break;
     
    case PT_LINE_STRIP:
     return D3DPT_LINESTRIP;
     break;
     
    case PT_TRIANGLES:
     return D3DPT_TRIANGLELIST;
     break;
     
    case PT_TRIANGLE_STRIP:
     return D3DPT_TRIANGLESTRIP;
     break;
     
    case PT_TRIANGLE_FAN:
     return D3DPT_TRIANGLEFAN;
     break;
     
    default:
     assert(0 && "getPrimitiveTopology::DirectX9 Unknown or unsupported primitive topology !");
     return -1;
     break;
   }
   break;
  case OpenGL:
  case OpenGLES:
   switch(pt)
   {
    case PT_POINTS:
     return GL_POINTS;
     break;
     
    case PT_LINES:
     return GL_LINES;
     break;
     
    case PT_LINE_STRIP:
     return GL_LINE_STRIP;
     break;
     
    case PT_TRIANGLES:
     return GL_TRIANGLES;
     break;
     
    case PT_TRIANGLE_STRIP:
     return GL_TRIANGLE_STRIP;
     break;
     
    case PT_TRIANGLE_FAN:
     return GL_TRIANGLE_FAN;
     break;
    default:
     assert(0 && "getPrimitiveTopology::OpenGL/ES Unknown or unsupported primitive topology !");
     return -1;
     break;
   }
   break;
  default:
   assert(0 && "getPrimitiveTopology() Unknown API !");
   return -1;
   break;
 }
}


A good compiler should optimize the dead code.
Also if you want to avoid the dependency on the graphics API headers, here are the necessary enums:


enum D3D_PRIMITIVE_TOPOLOGY
{
 D3D_PRIMITIVE_TOPOLOGY_UNDEFINED = 0,
 D3D_PRIMITIVE_TOPOLOGY_POINTLIST = 1,
 D3D_PRIMITIVE_TOPOLOGY_LINELIST = 2,
 D3D_PRIMITIVE_TOPOLOGY_LINESTRIP = 3,
 D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST = 4,
 D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP = 5
};

enum D3DPRIMITIVETYPE
{
 D3DPT_POINTLIST = 1,
 D3DPT_LINELIST = 2,
 D3DPT_LINESTRIP = 3,
 D3DPT_TRIANGLELIST = 4,
 D3DPT_TRIANGLESTRIP = 5,
 D3DPT_TRIANGLEFAN = 6,
 D3DPT_FORCE_DWORD = 0x7fffffff,
};

enum GLPrimitiveTopology
{
 GL_POINTS                        = 0x0000,
 GL_LINES                         = 0x0001,
 GL_LINE_LOOP                     = 0x0002,
 GL_LINE_STRIP                    = 0x0003,
 GL_TRIANGLES                     = 0x0004,
 GL_TRIANGLE_STRIP                = 0x0005,
 GL_TRIANGLE_FAN                  = 0x0006,
 GL_QUADS                         = 0x0007,
 GL_QUAD_STRIP                    = 0x0008,
 GL_POLYGON                       = 0x0009,
};

If you define the native types yourself, watch out for conflicts !

lundi 11 octobre 2010

What do programmers do when they are bored ?

Ok so I have not posted in a long time because I've been very busy with my uni stuff; so I thought I might share a nice little trick I developed quickly this morning.

Yes I was very bored and skimming through DXUT source file when I spotted those:
#define SET_ACCESSOR( x, y )       inline void Set##y( x t )   { DXUTLock l; m_state.m_##y = t; };
#define GET_ACCESSOR( x, y )       inline x Get##y()           { DXUTLock l; return m_state.m_##y; };
#define GET_SET_ACCESSOR( x, y )   SET_ACCESSOR( x, y ) GET_ACCESSOR( x, y )

#define SETP_ACCESSOR( x, y )      inline void Set##y( x* t )  { DXUTLock l; m_state.m_##y = *t; };
#define GETP_ACCESSOR( x, y )      inline x* Get##y()          { DXUTLock l; return &m_state.m_##y; };
#define GETP_SETP_ACCESSOR( x, y ) SETP_ACCESSOR( x, y ) GETP_ACCESSOR( x, y )

and I instantly wanted the same type of macros, except with all the genericity and safety of C++0x.

Setters were a walk in the park:
#define Nitro_PropertySet(name)    inline void set##name(decltype(m##name) t) { m##name = t; }

Getters not so much; I initially thought that I could use the trailing-type feature of C++0x to get an auto-magic return type, unfortunately it behaves differently and would not accept the member name for an answer.
So I used a little trick, I declare a template structure with a default template parameter (the decltype with the member name), add a typedef, inject the whole in the inline getters and there it is !

#define Nitro_PropertyGet(name)    template < typename T = decltype(m##name)> struct __##name { typedef typename T type; }; inline __##name<>::type get##name() { return m##name; }

For a member named mNumSplits, that expands to:
 template < typename T = decltype(mNumSplits)>
 struct __mNumSplits { typedef typename T type; };
 inline __mNumSplits<>::type getNumSplits() { return mNumSplits; }

Neat. And all that while eating breakfast :)

mercredi 1 septembre 2010

A horrible hack turned safe thanks to templates.

While coding some shader implementation for DirectX 11 I found myself writing the same code over and over for each shader type (Vertex, Fragment, Geometry, Hull, Domain etc...) and I figured I should make it more generic.

The only problem is that Direct3D 11 has a separate function to create each type of shader, so in order to make it generic I originally came up with a solution which I qualify as "horrible hack".

In DirectX 11 the ID3D11Device has some methods like:
HRESULT __stdcall ID3D11Device::CreateVertexShader(
    [in]   const void *pShaderBytecode,
    [in]   SIZE_T BytecodeLength,
    [in]   ID3D11ClassLinkage *pClassLinkage,
    [out]  ID3D11VertexShader **ppVertexShader);


    HRESULT __stdcall ID3D11Device::CreatePixelShader(
    [in]   const void *pShaderBytecode,
    [in]   SIZE_T BytecodeLength,
    [in]   ID3D11ClassLinkage *pClassLinkage,
    [out]  ID3D11PixelShader **ppPixelShader
    );

And ID3D11VertexShader and ID3D11PixelShader are both inheriting publicly from ID3D11DeviceChild:

ID3D11VertexShader : public ID3D11DeviceChild { ... }
    ID3D11PixelShader : public ID3D11DeviceChild { ... }

Now the trick was to define a function pointer CreateShader that has the following prototype:

typedef HRESULT (__stdcall ID3D11Device::*CreateShader)(const void *,SIZE_T, ID3D11ClassLinkage*,ID3D11DeviceChild**);

The only difference is the last argument which points to the superclass of the shader classes.
Now I only have to pass this function pointer pointing to the right method of the ID3D11Device when I create my shader.
The problem with that is that I have to pass the shader as its base type, which is supposed to work according to the rules of C++ except, however the (wise) compiler complained when I tried to do it implicitly.
That forced me to create a ID3D11DeviceChild* and assign the ID3D11*Shader to it, then pass it to the method.
In other word it look even more horrible; that's where templates come to help.

I decided to template the loadShader method, and use the shader type to select the appropriate ID3D11::Create* method.
Since function templates cannot have defaults, I created a helper structure:

template <class Shader>
struct CreateShaderHelper
{
 typedef typename HRESULT (__stdcall ID3D11Device::*FuncType)(const void *,SIZE_T, ClassLinkage*,Shader**);
};
Now I can re-write the loadShader method like this:

template <class Shader>
int loadShader(const char* name,
        const char* defines,
        const char* profile,
        typename CreateShaderHelper<Shader>::FuncType shader_creator,
        ConstantBuffer& cbuff,
        Shader*& outShader,
        ID3D11ShaderReflection*& outShaderReflect)
{
    ...
    if ((device->*shader_creator)(shader_buffer->GetBufferPointer(),shader_buffer->GetBufferSize(),&outShader) == S_OK)
    {
         ...
    }
}

lundi 30 août 2010

RenderTarget or Camera centric engine design ?

The engine I'm currently working on has a RenderTarget centric design, a lot like Ogre's.
A RenderTarget centric design means that all the rendering is done around RenderTargets:
The rendering window is a render target, the shadow maps are render targets etc...

Essentially a render target can have viewports, and each viewport has a camera.
This is good for doing things like multi-viewports views in games (like split screens), and it's easy to just add a viewport to a render target and configure a camera for it.
An advantage of this technique is that it minimizes render target changes, you set the render target, collect all viewports and cameras associated with it, then get all the visible objects and render them; rince repeat.
Obviously the downside is a lot of changes in camera matrices and viewports.

Recently I have acquired the book Game Engine gems 1, and in particular the gem by Colt McAnlis from Blizzard Entertainment caught my attention.
He describes a camera-centric engine design for multi-threaded rendering.
In his design everything is a camera and his association is done via a RenderView structure:
The RenderView struct groups the camera, frustum, render target and all the rendering commands.
This allows him to build drawing command buffers in parallel and then submit them to the API, grouped to minimize state changes.

At first I was bit against his design (after all we're all a bit reluctant to changes especially after working so long on a different design that does the job), but I realized that a camera-centric approach might facilitate dealing with things like portal-rendering which my design didn't account for at all.
That doesn't mean it's not possible to do with my current engine, just that I didn't think of that and I believe that a camera-centric design makes more sense; every camera contribute their view data to the final scene, including portal cameras.

In conclusion, RenderTarget centric design starts from a render target and move toward cameras, whereas camera-centric designs move from cameras to render targets.
Both design are good, and help structure the rendering engine, but a camera-centric design might reflect more how we think about cameras in real life; at least compared to the RenderTarget design which might seem backwards.

jeudi 5 août 2010

Hot config for people looking to spend big

If you are thinking of upgrading or buying a new computer, have a load of cash, and want to be future proof for a while (read >= 5 years) then this configuration would suit you:

Motherboard:EVGA Classified Super Record 2 (SR-2) about AUD1000
Processors: 2x Intel Xeon X5680 Gulftown, 3.33GHz, Six Core hyper-threaded about AUD2000 each
RAM: 12 hand-picked, hand-tested DIMMs, 48GB of ultra-high capacity at 1,900MHz, CL8 G.Skill Ripjaws
Power Supply: Thermaltake ToughPower 1500W PSU about AUD500
GPUs: 4x ATI Radeon HD5870 2GB OR 2x ATI Radeon HD5970 2GB (optional 1x Nvidia GTX480 (PhysX + 3D)) roughly AUD2000

And that does not even include the case (which will have to specifically support that over-sized HPTX motherboard) nor its cooling.
For a case (that does not exist yet), have a look at Mick64's dream case on extreme system or this Lian Li case.

And if you do build something like that, don't forget to post pictures and brag about it in forums, you deserve it :)

vendredi 23 juillet 2010

Mouse ballistics and OS X

Tonight I finished downloading CounterStrike:Source on my Mac
and thought of playing for a few hours, to see how it goes.

First impression was good, the game ran ok on my MacBook Pro 15" (with an Nvidia 9400M I don't expect miracles anyway), but then it happened: I kept on getting fragged after putting half of my clip in the environment (Player vs Environment took all its sense) instead of on the bad guys.

That was the last straw, I've had enough of the ridiculous mouse ballistics on OS X that literally ruined my game (I know it's an easy excuse to blame the OS =P, but trust me it was frustrating not even to mention painful).

The problem is that an operating system does not serve raw mouse value to its program (although you can access it), but applies what is called "ballistics" to the pointer (also known as mouse acceleration curve).

The ballistics applied determines precisely how a mouse "handles", and that's why the same mouse behaves differently under OS X, Windows or Linux (however some people report not noticing a difference).

On OS X, I found that the mouse feels sticky, I have to yank it a bit to get it to move.
And since I have to make such an abrupt movement to compensate for the stickiness,
I tend to overshoot the target when the mouse starts flying across the screen.
No matter how hard or often I try, I either overshoot, or stop too soon.
And that's not only in games, I often catch myself cursing after misplacing the caret in XCode, or clicking on the close button of a window instead of minimising.
It appears Apple brought this shame on its OS only recently, and I wish they fix it (as it's clearly broken); or at least bring the option to change the feel of the mouse.

Note that this applies only to external mice that I connect to my Mac, the built-in trackpad feels just right, and I usually use it instead; but then again how am I going to frag with a track pad ?

vendredi 16 juillet 2010

Hello World

Hello and welcome,

Every programmer learning a new language starts with some form of Hello World program; here's mine, although blogspot is no new language (English would be):

std::cout << "Hello and welcome"  << std::endl;

Seriously though, I intend to use this space to post my thought and comments mostly about technology, coding (especiallty C-based languages) and maybe a personal thing or two there and there as well.

Till next post, exit(0);