Thread overview | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
July 06, 2007 Resource Management... Howto? | ||||
---|---|---|---|---|
| ||||
Hi there. Lets assume I have a number of resources (audio, textures, fonts, etc) that I am trying to keep track of. I want a number of things to happen: 1.) When something tries to load a resource that isn't already loaded the resource will be loaded. The resource will be stored in an associative data structure so resources will be easy to find. "Something" will be handed back to the requesting code... maybe an int handle, or an object... I don't know yet. 2.) If an resource is already loaded then it will be found in the associative data structure and "something" will be handed back to the calling code. 3.) The resource will automatically be freed (calling any appropriate GL call to free GPU resources) when code is no longer referencing the resource... I have been thinking about this a lot, and I might have an idea how to do this.. I could have something like: auto h = texture.loadTexture(...); ... use the texture ... texture.releaseTexture(h); and texture keeps a reference count and when it drops to zero it is removed from the data structure and appropriate actions are taken to release any system resources. What I would like is to not have a texture.releaseTexture(). I would like that to be automatic. Perhaps "h" could be an object with a destructor that automatically calls "releaseTexture()"? Not sure of the structure of this.... and what would be best is to have one resource manager that keeps track of everything loaded... auto h = resource.loadResource(); but this gets tricky because how do you know what type of resource you are loading, and how do you set the parameters of the resource. If anyone has any comments, questions, suggestions, sample code, etc. it would be greatly appreciated. Thanks! Sam |
July 06, 2007 Re: Resource Management... Howto? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Samuel Winchenbach | "Samuel Winchenbach" <swinchen@eece.maine.edu> wrote in message news:f6m75n$22hh$1@digitalmars.com... > > I could have something like: > auto h = texture.loadTexture(...); > ... use the texture ... > texture.releaseTexture(h); > > and texture keeps a reference count and when it drops to zero it is removed from the data structure and appropriate actions are taken to release any system resources. > > What I would like is to not have a texture.releaseTexture(). I would like that to be automatic. Perhaps "h" could be an object with a destructor that automatically calls "releaseTexture()"? Not sure of the structure of this.... You have the basic idea right. Say one of your resource classes (for textures) looks like this: class Texture { private static Texture[char[]] Textures; public static Texture opIndex(char[] name) { if(auto tex = name in Textures) { (*tex).mRefCount++; return *tex; } Texture t = new Texture(name); Textures[name] = t; return t; } public static void release(Texture t) { t.mRefCount--; if(t.mRefCount == 0) { t.releaseResources(); Textures.remove(t); delete t; // optional } } private int mRefCount = 1; private this(char[] name) { // find and load the texture } private void releaseResources() { // you know what to do here } } This way, all you have to do is write "Texture["clouds"]" and it'll look it up in the AA, and if it's not there, automatically load it. Then when you're done, you call "Texture.release(t)" so that it can remove the texture from the Textures AA. Note that every time you access the texture list with the static opIndex, it increments the ref count if the texture is already in the list, so you'll have to call .release on any ref that you get out of it. Now comes the auto-destruction. What you can do is make a little scope class which controls the destruction: scope class TextureRef { private Texture mTexture; public this(Texture t) { mTexture = t; } ~this() { Texture.release(t); } public Texture tex() { return mTexture; } } Then you'd use it like this: void foo() { scope t = new TextureRef(Texture["clouds"]); // do crap with t.tex } When the function leaves, t will have its destructor called, and so the reference count will be decremented. Note that using scope classes can be very efficient, since when you have the pattern "scope x = new X", it will be allocated on the stack, and no heap allocation is made. This means the TextureRef class is very lightweight. Of course, with this layout, there's nothing binding you to using the TextureRef class; you can do it manually too, if you have i.e. a texture that needs to outlive a single function call. auto t = Texture["clouds"]; ... Texture.release(t); |
July 06, 2007 Re: Resource Management... Howto? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jarrett Billingsley | I've been thinking about a better way to do this. How about we (ab)use the GC? That is, each returned resource with the same index contains a pointer to some throwaway object. This object's address is stored together with the AA itself, but encoded somehow (negative? xor with a constant?) so that the GC doesn't find this relation. Now, whenever all resources of that type have been deleted, and the GC runs, the object will not have anything obvious pointing towards it anymore; thus, the GC will delete it. Before that, it will call the object's destructor, which, in turn, will delete the object's entry from the AA :) --downs PS: I have no idea if that could work; also, you'd be relying on the GC which the spec says _not_ to rely upon. Evil :) PPS: Not guaranteed to work. Use at own risk. |
July 07, 2007 Re: Resource Management... Howto? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jarrett Billingsley | Thanks Jarrett! I sort of went with a hybrid approach (for now) module resource; class TextureHandle { private Texture mTexture; private char[] mName; this(texture aTexture, char[] aName) { mTexture = aTexture; mName = aName; } ~this() { mTexture.mRef--; if (mTexture.mRef == 0) { mTexture.freeGPUtexture(); textureList.remove(aName); delete mTexture; } } } TextureHandle loadTexture(char[] aName) { if (auto t = (aName in textureList)) { (*t).mRef++; return new TextureHandle(*t, aName); } auto t = new Texture(); textureList[aName] = t; t.mRef++; return new TextureHandle(t, aName); } private { Texture[char[]] textureList; class Texture { private uint mRef = 0; this() { } ~this() { } void freeGPUtexture() { } } } I am trying to think ahead and see if this is what I really want. Eventually I will want to implement a file that contains all the various resources. I am not sure if I will run into a scope problem with that or not. I guess I just need to play around with it and see. :) Somehow it also seems I like should be able to make this more generic... instead of having a texture handle, have a generic resource handle. Overall it makes a LOT more sense to me in D than when I tried to do the same thing in C++ :) Sam |
July 07, 2007 Re: Resource Management... Howto? | ||||
---|---|---|---|---|
| ||||
Posted in reply to downs | downs wrote:
> I've been thinking about a better way to do this.
> How about we (ab)use the GC? That is, each returned resource with the same index contains a pointer to some throwaway object. This object's address is stored together with the AA itself, but encoded somehow (negative? xor with a constant?) so that the GC doesn't find this relation.
> Now, whenever all resources of that type have been deleted, and the GC runs, the object will not have anything obvious pointing towards it anymore; thus, the GC will delete it. Before that, it will call the object's destructor, which, in turn, will delete the object's entry from the AA :)
> --downs
>
> PS: I have no idea if that could work; also, you'd be relying on the GC which the spec says _not_ to rely upon. Evil :)
> PPS: Not guaranteed to work. Use at own risk.
Wow, I think that might be a little advanced for me. I am on approximately week 2 of D. Coming from ANSI C (with a touch of C++) this is quite a shock for me. If you have any example code of this in action I would love to see it though :)
Sam
|
July 07, 2007 Re: Resource Management... Howto? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Samuel Winchenbach | Samuel Winchenbach Wrote: > downs wrote: > > I've been thinking about a better way to do this. > > How about we (ab)use the GC? That is, each returned resource with the > > same index contains a pointer to some throwaway object. This object's > > address is stored together with the AA itself, but encoded somehow > > (negative? xor with a constant?) so that the GC doesn't find this relation. > > Now, whenever all resources of that type have been deleted, and the GC > > runs, the object will not have anything obvious pointing towards it > > anymore; thus, the GC will delete it. Before that, it will call the > > object's destructor, which, in turn, will delete the object's entry from > > the AA :) > > --downs > > > > PS: I have no idea if that could work; also, you'd be relying on the GC > > which the spec says _not_ to rely upon. Evil :) > > PPS: Not guaranteed to work. Use at own risk. > > > Wow, I think that might be a little advanced for me. I am on approximately week 2 of D. Coming from ANSI C (with a touch of C++) this is quite a shock for me. If you have any example code of this in action I would love to see it though :) > > Sam Sure .. I whipped something up that seems to work. import std.stdio, std.gc; typedef size_t hidden; hidden hide(Object ptr) { return cast(hidden)((cast(size_t)cast(void*)ptr)^0); } /// xor with 0 equals negation Object show(hidden h) { return cast(Object)(h^0); } class refcounter { hidden ptr; this() { ptr=hide(new class(this) { refcounter rc; this(refcounter r) { rc=r; } ~this() { writefln("Destructor called, telling refcounter to clean up"); rc.cleanUp; } }); } Object issueReference() { return new class(show(ptr)) { Object reftest; this(Object obj) { reftest=obj; writefln("Reference counted object constructed"); } ~this() { writefln("Reference counted object destroyed"); } }; } void cleanUp() { writefln("CleanUp called. The real deal would now remove the resource from its buffer."); } } void main() { auto r=new refcounter; auto a=r.issueReference(); auto b=r.issueReference; writefln("When calling fullCollect here, nothing will happen."); std.gc.fullCollect; writefln("Now delete the objects."); delete a; delete b; writefln("Again, fullCollect. But this time: "); std.gc.fullCollect; writefln("Exiting"); } And yeah, it's a little advanced. That's because proper automated reference counting is impossible in D because of missing copy semantics - it's impossible to track the duplication of objects. |
July 07, 2007 Re: Resource Management... Howto? | ||||
---|---|---|---|---|
| ||||
Posted in reply to downs | downs wrote: > hidden hide(Object ptr) { return cast(hidden)((cast(size_t)cast(void*)ptr)^0); } /// xor with 0 equals negation Xor with 0? That's a no-op: version (Tango) import tango.stdc.stdio; void main() { printf("%d\n", 0 ^ 0); printf("%d\n", 1 ^ 0); printf("%d\n", (-1) ^ 0); printf("%d\n", 2 ^ 0); printf("%d\n", (-2) ^ 0); printf("%d\n", 1000 ^ 0); printf("%d\n", (-1000) ^ 0); } If you want bitwise negation, use the ~ unary operator. -- Remove ".doesnotlike.spam" from the mail address. |
July 07, 2007 Re: Resource Management... Howto? | ||||
---|---|---|---|---|
| ||||
Posted in reply to downs | downs wrote:
> This object's address is stored together with the AA itself, but encoded somehow (negative? xor with a constant?) so that the GC doesn't find this relation.
std.gc has some functions to hide pointers from the collector, perhaps one can use these?
|
July 07, 2007 Re: Resource Management... Howto? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Deewiant | Deewiant wrote:
> downs wrote:
>
>>hidden hide(Object ptr) { return cast(hidden)((cast(size_t)cast(void*)ptr)^0); } /// xor with 0 equals negation
>
>
> Xor with 0? That's a no-op:
>
> version (Tango) import tango.stdc.stdio;
>
> void main() {
> printf("%d\n", 0 ^ 0);
> printf("%d\n", 1 ^ 0);
> printf("%d\n", (-1) ^ 0);
> printf("%d\n", 2 ^ 0);
> printf("%d\n", (-2) ^ 0);
> printf("%d\n", 1000 ^ 0);
> printf("%d\n", (-1000) ^ 0);
> }
>
> If you want bitwise negation, use the ~ unary operator.
>
Stupid me.
Sorry. The reason it still worked was probably because the GC doesn't consider size_t something that can point somewhere, so it didn't consider it when searching for references.
What I meant to do was probably along the lines of ^-1.
Changed to ~. Thanks :)
--downs
|
July 07, 2007 Re: Resource Management... Howto? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Samuel Winchenbach | "Samuel Winchenbach" <swinchen@eece.maine.edu> wrote in message news:f6o2k2$2gu6$1@digitalmars.com... > class TextureHandle > { > private Texture mTexture; > private char[] mName; > this(texture aTexture, char[] aName) > { > mTexture = aTexture; > mName = aName; > > } > ~this() > { > mTexture.mRef--; > if (mTexture.mRef == 0) > { > mTexture.freeGPUtexture(); > textureList.remove(aName); > delete mTexture; > } > } > > } The only issue with this is that _technically_ what you're doing in the destructor is illegal. Destructors of non-scope classes aren't guaranteed to be called (i.e. on program exit, they might not be, but the GC will definitely call them). Furthermore, the order of destruction is undefined, meaning that on program exit, mTexture might be destroyed before this, causing a nasty access violation when you try to do "mTexture.ref--". However, you can avoid this ugly nondeterministic-ness by always using scope references to TextureHandle (you can have scope references to non-scope classes, too), or by keeping all instances of TextureHandle in a static/global list which you clean up in a "static ~this()" -- when static dtors are called, the GC has not yet cleaned up, so it's still legal to access reference members in class dtors. |
Copyright © 1999-2021 by the D Language Foundation