Releasing IDirect3D8 Type

This is a discussion on Releasing IDirect3D8 Type within the Game Programming forums, part of the General Programming Boards category; Hullo hullo :-) Just to let you know, after finishing typing this, I realized it's pretty long. :-) I've recently ...

  1. #1
    Epo
    Epo is offline
    Registered User
    Join Date
    Jun 2003
    Posts
    361

    Releasing IDirect3D8 Type

    Hullo hullo :-)

    Just to let you know, after finishing typing this, I realized it's pretty long. :-)

    I've recently moved to bigger and badder things, but not wanting to have to rewrite my entire D3D engine, I just made copies of my .cpp files and threw a new project together with the intent of editing the old ones to meet my new needs.

    Unlike I expected, this actually went pretty smoothly.

    But an error came up with my new code that wasn't coming up before, even though all I did was delete code (no additions yet).

    Here's some really simplified code of my scenario (Just assume all links/headers I don't write in are included as well since that's not what the problem comes from):
    Note: The key fact is that m_pD3DDevice in both classes point to the same thing
    Code:
    Main.cpp
    int main(void)
    {
    	Class1 *MyClass1 = NULL;
    	MyClass1 = new Class1();
    
    	if(MyClass1 != NULL)
    	{
    		delete MyClass1;
    		MyClass1 = NULL;
    	}
    
    	return 0;
    }
    The Two Classes
    class Class1
    {
    public:
    	Class1(void)
    	{
    		m_pD3DDevice = NULL;
    		MyClass2 = NULL;
    
    		MyClass2 = new Class2(m_pD3DDevice);
    	}
    	virtual ~Class1(void)
    	{
    		if(MyClass2 != NULL)
    		{
    			// This line, fired before the next if,
    			// will cause MyClass1's destructor to
    			// fire. I'm pretty sure this is what's
    			// causing the error (more description soon)
    			delete MyClass2;
    			MyClass2 = NULL;
    		}
    		
    		if(m_pD3DDevice != NULL)
    		{
    			m_pD3DDevice->Release();
    			m_pD3DDevice = NULL;
    		}
    	}
    private:
    	IDirect3DDevice8 *m_pD3DDevice;
    	Class2 *MyClass2;
    }
    
    class Class2
    {
    public:
    	Class2(IDirect3DDevice8 *pD3DDevice)
    	{
    		m_pD3DDevice = pD3DDevice;
    	}
    	virtual ~Class2(void)
    	{
    		// This is the central problem I'm focussing on.
    		// When these 5 lines are here (below) I get
    		// the windows error when I'm trying to exit
    		// (The "Send Report to Microsoft" thing).
    		// If I take them out, it all runs smoothly...
    		if(m_pD3DDevice != NULL)
    		{
    			m_pD3DDevice->Release();
    			m_pD3DDevice = NULL;
    		}
    	}
    private:
    	IDirect3DDevice8 *m_pD3DDevice;
    }
    Okay, I think that should all be rather fine. It might look a tad confusing at first, but all it really is, is two classes pointing to the same address.

    So, the problem, I delete and NULL m_pD3DDevice in the destructor of Class2. Then, the destructor of Class1 tries to finish what it's doing, and it hits the If statement about it's own m_pD3DDevice.

    Now that should technically already be NULL shouldn't it? Because I think the problem that's arising is from this line, and for some reason the program is still trying to do something with that variable, which causes the "Send Error Report" from windows.

    What I'm wondering...
    1) Is it okay to just remove the whole If statement from Class2 since Class1 will eventually delete and NULL it anyways? Will there be any reprecussions to Class2's member variable?
    2) Is there a way to keep them both in? Because in my last project (the one I'm taking this code from), it's always been like that, and I've never encountered this problem.
    3) How come the If statement in Class1's destructor isn't catching that it's already NULL?


    Thanks for reading this all, it seems like a minor problem not worth such a long question being asked.

  2. #2
    Epo
    Epo is offline
    Registered User
    Join Date
    Jun 2003
    Posts
    361
    Today I learned the pitfalls of trying to find a fast way around using existing code.

    Opening an existing .cpp file in a new project, doing "Save As" to a new location, editing that new file, and saving it. Well, apparently, the initial .cpp file will also be altered.

    What did I learn? Don't be lazy, create new .cpp and .h files in your new project, then copy/paste the code.

    So much for having an engine. Ah well, it shouldn't take too too long before it's back to how it was supposed to be.

  3. #3
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,596
    There are several problems in making IDirect3DDevice a private member. First, nearly every function in the D3DX library and in D3D itself require that interface pointer. This means that you will have to pass this along to every function that needs it. Making it private means that you must create a public function or at the very least protected function just to return the interface pointer. This will translate into a lot of function call overhead just for the sake of attempting to be pure C++ and OOP minded. In this case I would, and have in my engine, made it a public member. In fact, it's part of a namespace and not even in a class. I did not see any benefit to the engine as a whole in making it a class. The entire Direct3D interface pointer is really quite simple and there is nothing gained by trying to encapsulate such a simple interface. If you attempt to encapsulate it your code is going to be slow because of function call overhead.

    Make the interface pointer public, and/or global. Yes I said global. This allows you to both create it and use it anywhere as long as you pass it along in either the Create(), Init(), or <gasp> the constructors to your classes. Passing the interface pointer from class to class is much more efficient than requiring every class that needs to use it to be passed a pointer to the base Direct3D class and then call the public function just to access one member.

    Also in your constructors I would not initialize anything except to NULL and 0. That is I would not instantiate any new classes or allocate any memory. If you do then you must use the try catch throw mechanisms which are very nice, but could complicate your code uneccesarily. Instantiate everything in a Create() function or Init() function. Those functions in turn can return error values back to the caller unlike constructors. Then the programmer can destroy the object that he/she tried to create by catching the error from your class and/or they can pop up an error message indicating what failed and why, or in Microsoft's case just pop up a message written in Greek to confuse the hell out of everyone. Also I would limit the number of classes you derive from bases. This will really confuse your code and probably won't add to your engine at all. Keep your derivations simple and use them only when absolutely necessary or when it exposes some important functionality to your engine that could not be gained otherwise.

    For instance in my code I have a base class called CTexturedPlane. This class encapsulates all functionality that any textured plane or textured quad would need. It will load textures, draw the object, etc. So when I made my CStaticBillboard (a non-animated billboard class) I simply derived from CTexturedPlane and in my virtual render function I created the billboard specific code. But all of the loading and init stuff is taken care of in CTexturedPlane.

    But with CPlanet which is essentially a textured sphere I did not derive it from CSphere. The problem with the derivation was that CPlanet was so different from CSphere that the derivation only complicated access. CPlanet did not do anything related to the CSphere. CSphere simply encapsulates creating a 3D sphere. It could be used for anything, not just planets. So deriving CPlanet from it made no sense. I simply instantiated CSphere inside of my CPlanet class.

    class CPlanet
    {
    CSphere PlanetModel;
    CSphere AtmosphereModel;
    ...


    };

    Then to render the sphere I set the texture - the texture coords are auto-computed inside of CSphere, device states, and tell CSphere to draw the thing. CSphere.GetMesh()->DrawSubset(0); I could even eliminate the GetMesh() function and simply make the mesh public for better efficiency.

    C++ is great, but it also can be your worst enemy.



    Just my two cents. Don't make your engine so OOP oriented that you can't even use the thing.
    Last edited by VirtualAce; 10-23-2004 at 11:57 PM.

  4. #4
    S Sang-drax's Avatar
    Join Date
    May 2002
    Location
    Göteborg, Sweden
    Posts
    2,072
    Quote Originally Posted by Epo
    3) How come the If statement in Class1's destructor isn't catching that it's already NULL?
    Because those are two different pointers -- just because you set one of them to 0, doesn't mean the other one will change.
    Last edited by Sang-drax : Tomorrow at 02:21 AM. Reason: Time travelling

  5. #5
    Epo
    Epo is offline
    Registered User
    Join Date
    Jun 2003
    Posts
    361
    Sang-drax:
    I think I see what you mean, I'm NULLing the pointer, not what it's actually pointing to right? (I hope)

    Bubba:
    You're right Hehe, who would've thought eh?

    Thinking about this some more, there's no real reason for this second class to exist. I was trying to be more organized, but it's just not turning out to be worth it (performance and time wise).

    I'm just wondering, now that I'm whittling it all down into one D3D class, there's no point in making the IDirect3DDevice public anymore is there? (I won't even mention making it global) ...(I still can't believe you used the G-word). And I have absolutely no clue what namespaces are, they've just never really come up. But I think I'll do some research just so I know what you're talking about in those few lines there.

    Thanks alot for the pointers (no pun intended) ... (well, maybe just a little one) ... I've almost got the code back to how it was, with the latest improvements.

  6. #6
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,596
    Global variables are not all bad. Especially when your engine requires them. For instance a global pointer to all objects is not a problem. It provides easy access by all who need it and it does not require a function call just to get at something that everyone and their mother needs.

    However if not everyone needs the variable or it is part of an object then don't make it global. Making the planet sphere's a global object in my engine would be a very bad idea. Only the planets and objects that use the sphere need access to it. In fact no one else should even touch the object so I made it protected to the CPlanet class, just in case one would need to derive from CPlanet....but they shouldn't ever need to. CSphere, however,is derived from CPrimitive since it is indeed a 3D geometric primitive. Making these global would be ludicrous.

    But I see no problem with placing the IDirect3DDevice interface pointer in a class as a public member or in a namespace.

    namespace MyD3D
    {
    IDirect3DDevice9 *Device;
    }

    To access::
    MyD3D::Device

    But how I'm using it comes from some books I've read. I make a global pointer to an IDirect3DDevice9 interface, then I pass the pointer to the Direct3DCreate() (actually another function in the namespace, not the actual D3D API Direct3DCreate()). After this is created then everyone has access to this. I pass this pointer along to every class that may need it. So essentially it only has file scope as it pertains to the main module. It does not have true global scope because it is not in a header - that would mean I would have to include that header for every class that uses it which is something I don't want to do.

    So 99% of my classe's Create() functions all take this form.

    void <class_name>::Create(IDirect3DDevice9 *_device,......)

    Each class in turn has a pointer to the device interface. The downside to this is that you could essentially destroy the interface by releasing it accidentally in a destructor, but as long as you do not do this only the pointer is destroyed when the object goes out of scope, not the interface the pointer points to.

    My entire setup is more of an object-render setup. Each object is responsible for rendering itself. This works well except for complex systems that share textures and characteristics like particle systems, sprites, etc. These are handled much differently and are all drawn in one render function. But those are the exception, the rest are handled in the class's render function. Most of them simply need the timeDelta passed to them so they can base their movements and updating based on frame time's.

    So far it all seems to work very well. The pointers will be turned into vector lists by the time the engine is done. The vector lists will be populated with the current object database for the current system. Based on that list, the actual rendering system will build a render list for objects that need to be rendered.

    The only problem I'm having is finding a good way to implement pixel shaders w/o having them so tied into one object. After all you might want to share an effect among objects yet the way it is setup in D3D is a bit different than that approach. I'm looking into creating huge effect files that encapsulate one effect applicable to all objects if needed complete with low-res or low-end versions of the same effect. The effects framework in D3D is a very nice way to do this.

    Perhaps we should join forces. Contact me via PM and I'll hook you up on my teamspeak program. It's far easier to discuss these things than chat about them.

  7. #7
    Epo
    Epo is offline
    Registered User
    Join Date
    Jun 2003
    Posts
    361
    Thanks for the tips, I'm definitely going to be having to move to a system like that eventually, but sadly, I have to put aside this current project for the moment.

    I haven't been around these boards in about a week due to exams and (I know I'll get hassled by lots of people for this but...) a Visual Basic *Shudders at the looks of disgust he knows he's receiving right now* project. For the sake of not having to spend a month greating a GUI, it just made sense

    But in just three solid days, I've made a ton of progress, so I'm hoping it's not too long before I can pick this back up and stun you all with the most intense 2D action ever! Heh (Or maybe something better). But seeing some of the screenies that Bubba's been posting, it'll still be a while before I'm anywhere near that level.

    Just wanted to say thanks again for the tips though, they've been huge in converting a flicker I liked to call my refresh rate into smoothly displayed scenery (Actually, not so smooth...I have some issues I'll be bringing up in the near future)

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 0
    Last Post: 03-20-2008, 07:59 AM
  2. Little Array Difficulty
    By G4B3 in forum C Programming
    Replies: 16
    Last Post: 03-19-2008, 12:59 AM
  3. Replies: 28
    Last Post: 07-16-2006, 11:35 PM
  4. Errors
    By Rhidian in forum C Programming
    Replies: 10
    Last Post: 04-04-2005, 12:22 PM
  5. gcc problem
    By bjdea1 in forum Linux Programming
    Replies: 13
    Last Post: 04-29-2002, 06:51 PM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21