Thread: Module Development for Game Design - By Jeff Verkoeyen

  1. #16
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Quote Originally Posted by Bubba
    I was not under the impression that COM was using virtual function calls since COM is a binary standard and not true C++.
    Even though everything is derived from IUnknown it is not the same as a C++ class.
    Please expand on this.
    COM is a binary standard indeed, but once that coincides with the way the MS C++ compiler lays out a C++ class that only has pure virtual functions.

    So, to define an interface, you simply do this:
    Code:
    struct IUnknown
    {
      virtual UINT __stdcall AddRef() = 0;
      virtual HRESULT __stdcall QueryInterface(void **ppv, REFIID iid) = 0;
      virtual UINT __stdcall  Release() = 0;
    };
    Without treating it specially in any way, the C++ compiler will emit code that is binary compatible with COM.

    A COM object is just a class that inherits any number of interfaces and implements all the functions inside.

    So yes, COM is just a binary standard, but it says about this:
    An interface pointer points to a memory location where the first thing is a vtable pointer. This vtable pointer points to a memory location that contains function pointers. To call a function through COM, you first push the interface pointer to the stack, followed by all the parameters, from left to right. Then you dereference the interface pointer. You add the function-specific offset to the resulting pointer and call to the resulting address. After the call returns, you don't need to clean up the stack.

    This is the exact way the MS compiler implements virtual function calls that use the __stdcall convention. The difference to the standard C convention is in the way parameters are passed, but not the call mechanism.

    The bottom line is that the COM call overhead is equal to a virtual function call.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  2. #17
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Thanks a million. I've not designed my own COM objects yet but that info is extremely useful.

    The point I'm making about games here is probably only relevant to those that require a lot of processing power for the graphics and game content. I've noticed extreme performance degradation as a result of copying structures during the main render loop, calling my own virtual functions within a render loop, and of course accessing anything on disk during the main render loop.

    So to circumvent the problem I've decided that since the only difference between objects is usually:

    • Their orientation in 3 space - translation, rotation, scaling, etc. - essentially their world transformation matrix
    • Their texture(s) - these should be maximized so as to wrap around the object - thus using 1 texture for a mesh.
    • Their primitive type info (some objects have multiple primitive types)
    • Their texture blend states and texture addressing modes
    • Their vertex type and/or FVF


    Because most objects only differ by these properties (there are probably more you can think of) it makes sense that one render function could be used to render the entire scene.
    This approach is much different than having a virtual render for objects or having a render function for each object. This method results in virtually no outside calls being made which results in faster renders.

    Look at your rendering code and you will see that most of them in Direct3D will look something like this:

    Code:
    void Object::Render(float fFrameTimeDelta)
    {
    
      D3DXMATRIX World,Translation,Rotation,Scaling;
      D3DXMatrixRotationYawPitchRoll(&Rotation,Object.Rot.x,Object.Rot.y,Object.Rot.z);
      D3DXMatrixScaling(&Scaling,Object.Scale.x,Object.Scale.y,Object.Scale.z);
      
      World=Scaling*Rotation;
      World._41=Object.Pos.x;
      World._42=Object.Pos.y;
      World._43=Object.Pos.z;
    
      Device->SetTransform(D3DTS_WORLD,&World);
    
      //Setup render/texture stage states for this object
      //...
    
      //Set texture
    
      //Draw Object
    }
    You can clearly see that nearly every object will follow this except for special effects which might also need to use the view matrix to invert it, etc. But for the most part objects all follow the same rendering path. Because of this it seems to me that one could place the information inside of what I would call object state blocks that would tell the renderer all the pertinent information relating to the current object.

    Also since most objects fall into similar categories (billboards, sprites, 3D objects, etc) it seems to me that they should be rendered in batches. As well since all objects must have a vertex buffer it seems plausible to use one huge vertex buffer and specify an index range in the object state block so the renderer knows which indexes in the master vertex buffer to use for this object. Primtive type could also be stored in this section as well or you could setup a structure that could handle multiple primitive types throughout a range of indexes.
    Textures would simply be an ID within the class stating which texture the object uses. The actual IDirect3DTexture9 interface would reside in the global list for the currenttly loaded level in memory. This means that any texture can be used on any number of objects but yet still only reside in memory exactly one time. The same holds true for sound effects, etc. I can see a great benefit to storing the actual texture in a list instead of creating a new IDirect3DTexture9 object relative to the object class. You would also be assured of the same texture only existing once and only being destroyed once....you could effectively destroy the list of textures at any time you wanted to and be assured that no other instances of the interface were lurking around in your code. Nice and centralized.

    I really think looking this stuff up based on indexes and using an incremental rendering approach with one huge vertex buffer would be much faster than calling virtual render functions for each type of object that has been derived from a base object. I could be wrong but I don't think I am.
    Last edited by VirtualAce; 02-13-2005 at 12:54 AM.

  3. #18
    Toaster Zach L.'s Avatar
    Join Date
    Aug 2001
    Posts
    2,686
    A quick note on the article itself:
    Its presenting a technique that is useful not just for game programming (I won't debate its usefulness for that purpose, as I don't do any game programming, but it is certainly useful elsewhere), but I don't think it presents the idea extraordinarily well. Those who know the basic technique will see obvious uses, but without a few more concrete examples of how to use it (not necessarily code), I don't see a programmer without much OO experience gaining much from it.

    Cheers
    The word rap as it applies to music is the result of a peculiar phonological rule which has stripped the word of its initial voiceless velar stop.

  4. #19
    Software Developer jverkoey's Avatar
    Join Date
    Feb 2003
    Location
    New York
    Posts
    1,905
    *nods* That's understandable (I wasn't aware the article was being posted and therefor didn't get a chance to bulk it up a bit as I'd written it earlier under the impression I was under a size limit....)

    However, here's a more detailed example (codewise), I'll add a graphical example later when I get home:

    Let's say in a program you have multiple different parts. In this case, we'll use a game, just because it's easiest to describe and everyone's played a game before (I'd hope).

    So, in this program, let's say we have a main menu, the game itself, credits, and an options screen.

    One way of going about deciding which section you are currently running is by using a switch statement:

    Code:
    enum GameStates { MAINMENU, GAME, CREDITS, OPTIONS };
    ....
    GameStates gameState;
    .....
    
    switch(gameState)
    {
    case MAINMENU:
        runMenu();
        break;
    case GAME:
        runGame();
        break;
    ....etc
    }
    This is a very watered down version, most games will have many more game states than just this, and as you can see, for every state you want to add you have to add yet another test to the game loop that gets tested every frame.

    The O notation for this sort of setup would be O(n) for every new module you want to add. This extra time is practically moot nowadays, but it also presents a problem with expandability and code reusability.

    However, with a module-based design, you can have as many game states as your heart desires and the only hit you'll have is just a single virtual function call, which, for a large game, will be much more advantageous to an incredibly large switch statement.

    So let's see a better example:

    We have the game loop, as described in the article, and the base class for each module. Using this, we can create the game states very easily:

    Code:
    // GameModule.h
    class GameModule : public Module
    {
        private:
        public:
            void Initialize();
            Module* ExecuteModule();
            void Shutdown();
    };
    extern Module* GameMod;
    Code:
    // GameModule.cpp
    #include "GameModule.h"
    Module* GameMod;
    
    void GameModule::Initialize()
    {
    // Initialization code
    }
    
    Module* GameModule::ExecuteModule()
    {
    // Execution code
        return GameMod;
    }
    
    void GameModule::Shutdown()
    {
    // Shutdown code
    }
    And then we need to initialize the GameMod pointer elsewhere at the startup of our program.

    But basically, this is the fundamental design using the module system, I will upload my template code that I use when starting new projects when I get home, and hopefully that will make the whole thing a bit easier to comprehend.
    Last edited by jverkoey; 02-23-2005 at 05:06 PM.

  5. #20
    Software Developer jverkoey's Avatar
    Join Date
    Feb 2003
    Location
    New York
    Posts
    1,905
    Ok, attached are the image and my template code I use in all of my games involving OpenGL. It is my template project and should be able to compile and create a program immediately. Assuming you want to actually use the code, however, you'll need a few files from my library, which are attached in a separate zip.

    -note-
    because of the stupidity of not being able to upload .zip files, you need to rename the two .txt files to .zip to open them correctly.

  6. #21
    Software Developer jverkoey's Avatar
    Join Date
    Feb 2003
    Location
    New York
    Posts
    1,905
    Here's the pic

  7. #22
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    But this still does not lend credence to the virtual function argument. So far in my projects I haven't found a place where I absolutely need to use a virtual function, so I haven't. All of my modules are separate and to add functionality you simply create classes and add them into the existing CD3DApp class.

    The CD3DApp class I've created sets everything up and rather than use a virtual render function and a virtual function call to figure out which texture to use, I use a global array. So texture switches amount to simply storing a texture ID for each texture and then setting the texture to that ID in the main loop. The more and more I code this stuff, the more I can see that there are about a billion ways to do it, but only a few that yield very good results. I'm still fighting for FPS in my games - the more optimization, the better. The fewer calls inside a render loop the better.

    I fail to see the validity of the argument based on your examples.

  8. #23
    Software Developer jverkoey's Avatar
    Join Date
    Feb 2003
    Location
    New York
    Posts
    1,905
    Yah, I can see what you mean, mainly from my own experiences with 2D games, I've found this system of development much easier than any other method I've ever used. The one drawback is that most of the time, 2D games aren't incredibly graphics intensive so it's hard to judge if one method is definitely faster than another one or not.

    It basically comes down to a matter of stylistic choice and which method you prefer to implement in your own projects. For projects where you need to scrape every bit of efficiency out of your code, this system probably wouldn't be the best design. But for other smaller projects, it provides a simple means of setup and encourages code reusability.

  9. #24
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Agreed. Good article and good discussion.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Find Injected DLLs In A Process?
    By pobri19 in forum Windows Programming
    Replies: 35
    Last Post: 02-06-2010, 09:53 AM
  2. Function basics
    By sjleonard in forum C++ Programming
    Replies: 15
    Last Post: 11-21-2001, 12:02 PM