C++ DX API, help me get it?

Started by
13 comments, last by Jason Z 10 years, 9 months ago

Ok so i'm coming from a .net background (multiple years of professional development).

I'm checking out microsoft DX11 samples and i just don't get it, am i missing something obvious & there's actually beauty there i'm not seeing? Or is this actually just flat ugly hard code for no reason? Is it legacy or are you actually supposed to write & consume APIs this way?

(just to be clear, this is half rant, half question, i'd be more than happy to learn my rant is unfounded & have this transition from a rant to an answered question, also i'm not saying i'm having a hard time, just to be clear i understand the sample perfectly fine, i just find it painstakingly hard for no reason).

Shed some light on all this for me

1) Why the hell is this so damn long & hard? I expected getting a DX11 window up & running (and rendering) with nothing inside to be a 10liner or so, maybe 20 tops, but not 250 like in the sample!

2) Why the typedefs? Seems to be adding confusion for no reason especially on simple types, am i missing something or is this just useless? typedef float FLOAT

3) Why no constructors? Objects are just declared and initialized element by element, meaning you remove 1 line you don't get a default, you don't even get an exception, you get a nice access violation!

4) Why all the if(failed(bla)) ? Why isn't code throwing?

5) Probably the same as 3 but, why no sensible default values for everything? Declaring any directX object seems to be a full time job & a 10 liner!

6) Why is everything taking a pointer? I get the point for large (or medium) objects but why for example does something like the feature level, which isn't a large object nor an array, and that you're likely to be using once (or hell, maybe twice!) in your whole application get passed by pointer? I'm new at C++ but unless i get it wrong it means you must create a (local or global) variable, assign a value to it, and pass a pointer to it, if it was by reference you could just pass in D3D_FEATURE_LEVEL_11_0 for example

7) Typedef question again, it's confusing enough for float => FLOAT, but hell LPVOID*, i can't believe people starting with C++ make it through this!

8) __uuidof, so you even need custom keywords to get a simple directX sample app going on???

I'm not trying to bash C++ here, the only things i can't bare in it are header files and compilation speed, but i just don't get that API design, is it flaws in the API or in the sample? Or am i just getting it wrong? Because if this is the right way to do it this sounds just horrible to me, 250 lines to do nothing, 3X that much for a rotating cube (out of which only 60 lines are rendering, and about 300 setting up the device . . . ). I just don't get it! Hell SharpDX didn't feel that way at all.

I'm waiting, please enlighten me!

Advertisement

Allow me to pick off part of #6:

D3D11CreateDeviceAndSwapChain has two parameters that accept pointers to D3D_FEATURE_LEVEL. The first one is a pointer because it is actually looking for an array of D3D_FEATURE_LEVEL (and the parameter following that one is the number of elements in the array). The second one is a pointer because it is an output parameter to where the feature level that was actually selected can be stored.

http://msdn.microsoft.com/en-us/library/windows/desktop/ff476083(v=vs.85).aspx

[EDIT]

Also, I expect that a lot of the reasons for the API being structured how it is are due to http://en.wikipedia.org/wiki/Component_Object_Model

1) Short version, modern languages and frameworks tend to do a lot of the work for you. This is the raw api.

2) typedef float FLOAT, might become typedef double FLOAT in the future, dont need to change as much later you can redefine FLOAT.

3) Windows has a long history with C, it tends to be the main focus when extending the windows apis, C has no constructors

4) see 3

5) see 3

6) see 3

7) see 3

8) see 3

1) A lot of the setup in the simple tutorials is just getting a window going. That's just the way it is in Win32: it's a crusty old C API that takes a lot of boilerplate just for basic functionality. There's no reason not to use a framework to handle this for you, whether it's an existing one like SDL or one of your own design.

2) It's inherited from the Windows API. Almost all API's do something like this, since C/C++ only makes loose guarantees about the size of types. Using typedefs can ensure that the API is always working with the same size type. This isn't really much of an issue for x86 Windows platforms, so you can usually ignore them and using native types or the types from stdint.h

3) D3D uses a light-weight form of COM. Supporting COM requires C compatibility, which means you're mostly limited to functionality exposed in C (before C99). This is why you have things like pointers instead of references for function parameters, and structs with no methods. However there are actually "C++" versions of some of the structures that have constructors defined. They're named the same with a "C" in front of them, for instance CD3D11_TEXTURE2D_DESC. It's also possible to make your own extension structures if you want.

4) Mostly because the older samples are written in more of a "C with classes" style as opposed to modern C++. The newer samples written for the Windows 8 SDK actually make heavy use of exceptions, smart pointers, and C++11 features. In my home codebase I just made a wrapper macro for D3D calls that checks the HRESULT for failure, and if it fails converts it to a string error message and stuffs it in an exception.

5) This is indeed the same reasoning as #3. It can definitely be pretty ugly at times.

6) Also the same as #3

7) Yeah that stuff is rooted in the Win32 API, and it's seriously ugly. I never use the typedefs in my own code.

8) This comes from D3D being a COM API. DXGI actually happens to be even heavier on the COM compared to D3D, hence it taking the interface GUID as a function parameter. However I'm pretty sure you don't have to use __uuidof if you don't want, it's just a convenience.

The reason SharpDX doesn't "feel" the same is because they wrap all of this in types and functions that convert the COM/Win32 idioms into patterns that are typical to C#. You can certainly do the same except with modern C++ concepts, if that's how you'd like to do it.

That's why I keep my distance from C++! Even in C# you need to write a lot of code just to set up a device. It is certainly nicer to look at, less * and & :D But once you begin writing your engine you can abstract away all that. In my own engine everything boils down to a call to DeviceManager.Initialize where I pass my own settings structure... and that's that.

--Avengers UTD Chronicles - My game development blog

As the others have already mentioned, you are comparing two different things. C++ has direct access to the D3D API, and hence has to do the heavy lifting to get anything going. But SharpDX is actually just a library that sits between you and the API to make things easier. Most people don't start from scratch in C++ either - for example, in the Hieroglyph 3 application framework, you just inherit from a class and your window setup, device setup, and all the other goodies are done without any additional code.

One thing not a lot of people realize is that most of the Windows API samples are written so that non-C and C++ programmers will understand them. For instance, when initializing structs the API samples will use ZeroMemory() despite the fact that = {} will do the same thing. However, non-C or C++ programmers won't necessarily understand that, so the explicit ZeroMemory() call is used instead.

Ok so as i expected most of it is to be blamed on legacy or COM, actually makes me quite happy to hear that.

Is there any library on the C++ side that does the same as SharpDX for .net? (keep the same low level API access, but wrap it in namespaced classes with default constructors etc, something that would feel more "modern C++ish" without being an engine but that would be a good base for starting one without doing my own wrappers on everything?)

4) Why all the if(failed(bla)) ? Why isn't code throwing?

NEVER use exceptions in C++! I love HRESULT error codes

6) Why is everything taking a pointer? I get the point for large (or medium) objects but why for example does something like the feature level, which isn't a large object nor an array, and that you're likely to be using once (or hell, maybe twice!) in your whole application get passed by pointer? I'm new at C++ but unless i get it wrong it means you must create a (local or global) variable, assign a value to it, and pass a pointer to it, if it was by reference you could just pass in D3D_FEATURE_LEVEL_11_0 for example

Use CComPtr(atlbase.h) if you dont like pointers...

LPVOID

well.... i really dont care about PVOID LPVOID FLOAT ect...


D3D is designed for performance and user-control.. If you dont like that then you can use something like irrlicht/ogre/panda/whatever

4) Why all the if(failed(bla)) ? Why isn't code throwing?

NEVER use exceptions in C++! I love HRESULT error codes

6) Why is everything taking a pointer? I get the point for large (or medium) objects but why for example does something like the feature level, which isn't a large object nor an array, and that you're likely to be using once (or hell, maybe twice!) in your whole application get passed by pointer? I'm new at C++ but unless i get it wrong it means you must create a (local or global) variable, assign a value to it, and pass a pointer to it, if it was by reference you could just pass in D3D_FEATURE_LEVEL_11_0 for example

Use CComPtr(atlbase.h) if you dont like pointers...

LPVOID

well.... i really dont care about PVOID LPVOID FLOAT ect...


D3D is designed for performance and user-control.. If you dont like that then you can use something like irrlicht/ogre/panda/whatever

None of this relates to performance at all, unless you consider "setting up directx" a performance critical part of any application where it's important to saveup nanoseconds in object instantiation and saving 4 bytes copies here & there? Performance is no reason here (i could get it if it was in per frame actions, but just not here), nor does it relate to user control at all. Anyway i already had my answers earlier on this thread, it's just com limitations & legacy code.

So now just looking for a thin wrapper around it, NOT an engine, something like a directX for actual C++ and not com/C.

Anyone could recommand such a thing? Something very thin where i could still refer to DX documentation, but just use it in a more "modern C++" way, C++11 is fine (within visual studio 2013's limitations)

This topic is closed to new replies.

Advertisement