Do I need to release() these textures?

Started by
7 comments, last by Jason Z 11 years, 1 month ago

So lets say I have a struct called 'Sprite.' Inside that struct is the member LPDIRECT3DTEXTURE9 texture.

Let's say I make an array of Sprite's that looks like: Sprite myArray[1000].

I want the same image to be loaded to all 1000 of myArray texture members.

I could make a for loop that uses 'LoadTexture' (My texture loading function) to load an image to each texture member. But this is a very taxing process and takes a noticable amount of time to complete. At the end of my program I would then release() each texture via for loop.

So the other option is to create a LPDIRECT3DTEXTURE9 defaultImage. Load an image to defaultImage, then run a loop that:

myArray.texture = defaultImage;

The load time is unnoticible using this method.

So here's my question:

Do I have to release() all 1000 textures in myArray or do I only have to release() defaultImage?

For the record, I've tried realease()ing all 1000. Get the Unhandled exception at 0x00430906 error and have to break.

Does that mean I'm doing something wrong, or that I only have to release the one? An explination as to why would be nice as well.

Thanks for the help!

Advertisement
Good practice is to AddRef defaultImage each time you assign it to myArray.texture. That way your object lifetime is properly managed.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

As a rule, for every resource Create(), you need a Dispose(). In your first example, you do 1000 Create()'s, (LoadTexture() is doing the Create() internally, but its still there) , so you'll need 1000 Dispose()'s, as each Sprite has its own unique texture object.

In the second example you create one texture that is shared amongst 1000 objects. This means there's one Texture, but 1000 references to it. Therefore, you need only 1 Dispose().

If all your sprite objects are going to be using the exact same texture, then definitely use the second method, otherwise you're using 999 times more texture memory and bandwidth for no reason.

I'm not sure if you've checked it out already, but it might be worthwhile looking into the built in sprite handling stuff of D3DX, such as ID3DXSprite. It's designed to be a lightweight way to draw lots of small textures to the screen and might be suitable for your application.

[size="2"]Currently working on an open world survival RPG - For info check out my Development blog:[size="2"] ByteWrangler

As a rule, for every resource Create(), you need a Dispose().


In the second example you create one texture that is shared amongst 1000
objects. This means there's one Texture, but 1000 references to it.
Therefore, you need only 1 Dispose().

In theory yes, but in practice you should, especially in your second case, use AddRef() every time you pass a texture to another object - so when its shared by 1000 object it would have a RefCount of 1000 (+1?), and every time one object gets destroyed or accepts another texture handle, you call Release() - so only if no object has a reference, the texture gets freed.

In the second example you create one texture that is shared amongst 1000 objects. This means there's one Texture, but 1000 references to it. Therefore, you need only 1 Dispose().

COM objects don't really work that way. Because they're internally reference counted, if anything needs to hold on to the object for any reason, that assignment should be accompanied by an AddRef. Then it can be safely Released later on, which will just decrement the reference count rather than destroying the object. When the final Release happens, the object is destroyed.

The reason for this is that if something else Releases the texture while your myArray (to use the example here) is still using it, the texture won't be destroyed and myArray can continue to safely use it.

So in other words, yes, Release the defaultTexture 1000 times as you currently are, but also add an AddRef when you set them to your myArray, and the Release won't crash, giving you proper COM object lifetimes.

You see this kind of pattern all over the design of D3D; e.g. GetSurfaceLevel will require a Release otherwise the surface leaks, and - more relevant here - ID3DXEffect will AddRef on a SetTexture call.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

In the second example you create one texture that is shared amongst 1000 objects. This means there's one Texture, but 1000 references to it. Therefore, you need only 1 Dispose().

COM objects don't really work that way. Because they're internally reference counted, if anything needs to hold on to the object for any reason, that assignment should be accompanied by an AddRef. Then it can be safely Released later on, which will just decrement the reference count rather than destroying the object. When the final Release happens, the object is destroyed.

The reason for this is that if something else Releases the texture while your myArray (to use the example here) is still using it, the texture won't be destroyed and myArray can continue to safely use it.

So in other words, yes, Release the defaultTexture 1000 times as you currently are, but also add an AddRef when you set them to your myArray, and the Release won't crash, giving you proper COM object lifetimes.

You see this kind of pattern all over the design of D3D; e.g. GetSurfaceLevel will require a Release otherwise the surface leaks, and - more relevant here - ID3DXEffect will AddRef on a SetTexture call.

Thanks for the help! Very informative. I'm new to development so I'll put some time into studying AddRef, as it sounds pretty vital.

But just for the sake of understanding, if I only release defaultImage like in the second example, is that causing a memory leak in myArray? Is it bad/dangerous coding or just improvable coding? (for the record I don't intend on settling for poor coding).

If you're referring to the second example in your original post ("So the other option is to create a LPDIRECT3DTEXTURE9 defaultImage. Load an image to defaultImage, then run a loop that: myArray.texture = defaultImage;") - no, it won't leak. This doesn't actually copy any data at all, so you still only have one copy of the texture. Instead each myArray.texture will point to the original texture (if this concept sounds strange to you then I recommend doing some reading/learning/exercises on pointers).

Where you get trouble is when you Release defaultImage. Each myArray.texture still points to defaultImage, but defaultImage is no longer valid for use. Any operation on myArray.texture will - if you're lucky - crash immediately. If you're unlucky it will continue working then give you weird behaviour (or a crash) some time later and you'll have no idea whatsoever where to even begin looking for the problem. This normally happens the day before a release deadline.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

If you're referring to the second example in your original post ("So the other option is to create a LPDIRECT3DTEXTURE9 defaultImage. Load an image to defaultImage, then run a loop that: myArray.texture = defaultImage;") - no, it won't leak. This doesn't actually copy any data at all, so you still only have one copy of the texture. Instead each myArray.texture will point to the original texture (if this concept sounds strange to you then I recommend doing some reading/learning/exercises on pointers).

Where you get trouble is when you Release defaultImage. Each myArray.texture still points to defaultImage, but defaultImage is no longer valid for use. Any operation on myArray.texture will - if you're lucky - crash immediately. If you're unlucky it will continue working then give you weird behaviour (or a crash) some time later and you'll have no idea whatsoever where to even begin looking for the problem. This normally happens the day before a release deadline.

Ah yes! And the reason nothing is copied is because LPDIRECT3DTEXTURE9 is a pointer! I forgot about that. Okay, thanks for taking the time not just to answer, but to help me understand as well.

Just to provide a counterpoint, it is also perfectly legal to only add one reference to the COM object and use it 1000 times - but then you are responsible to ensure that all 1000 of those copies are not released by some other code and that your single reference is only released when all 1000 copies are ready to be retired. If you build a system around your copies that properly encapsulates all of the references in a safe way, then it is possible to (mis)use the object reference system in different ways.

That being said, the advice given by the others is perfectly correct - you probably should add a reference to each copy of the item in the array!

This topic is closed to new replies.

Advertisement