Home » Community » Forums » General Programming » Type-safe GUID handles and data hiding
  Intel sponsors gamedev.net search:   
[Control Panel] [Register] [Bookmarks] [Who's Online] [Active Topics] [Stats] [FAQ] [Search]

Add Forum to Favorites |  Send Topic To a Friend | View Forum FAQ | Track this topic

Page:   1 2 »»

 Last Thread Next Thread 
 Type-safe GUID handles and data hiding
Post New Topic  Post Reply 
In my current renderer design I am employing the use of GUIDs to hide the actual (API) implementation of different resources, e.g:

typedef unsigned int GUID_Type;

GUID_Type myTexture;
renderer->createTexture ( &myTexture, creationParams );



My recent qualm with this is that I could do something like this:

renderer->bindTexture( myShaderHandle );

Clearly my design isn't type-safe, and could result in run-time silliness.
So I thought I would wrap each type of resource GUID in a different class to gain typesafety:

class TextureHandle {
public:
    GUID_Type id;
};

Now class instances are passed to the renderer, any attempt to muddle different types of handles will result in a nice big compile-time error.

But now I'm wondering about data hiding, the client could easily modify the id of a resource and once again introduce a run-time bug.
So, if I'm concerned with data hiding the requirements become:
The user needs read-only access to the id,
But the renderer needs read/write access to the id.

I could hide id behind getters and setters but that doesnt really get me anywhere does it, and everybody knows getters and setters are bad .

Another possibility is making the renderer a friend of each resource handle class. The problem with this is that the renderer is an abstract class, I could make the base class a friend for each resource handle but that wont help the derived implementations of renderer without them jumping through (overhead adding) hoops to access the handles' GUIDs.


Any ideas?
Perhaps data-hiding isnt worth it in this case, if the client wants to mangle the id then thats their problem?

I'd be intrigued to hear peoples oppinions about this or alternate solutions.

 User Rating: 1695   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

The biggest issue is your requirement that the renderer can alter IDs.

If that weren't an issue, you might be able to get away with having an inner class that wraps around the ID. The issue becomes keeping the inner class and outter class in synch when the renderer changes the ID.

Probably not the best imlpementation ... but it could work.

 User Rating: 1657   |  Rate This User  Send Private MessageView ProfileView Journal Report this Post to a Moderator | Link

#include <iostream>

typedef unsigned int GUID_Type;

class TextureHandler {
public:
	class Reader {
	friend TextureHandler;
	public:
		const GUID_Type getID() const;

	private:
		void setID(GUID_Type id);
		GUID_Type id;
	};

	TextureHandler(GUID_Type id);
	const Reader &getReader() const;
	void setID(GUID_Type id);
	GUID_Type getID();

private:
	Reader r;
};

typedef const TextureHandler::Reader& PublicTextureHandler;

const GUID_Type TextureHandler::Reader::getID() const {
	return id;
}

void TextureHandler::Reader::setID(GUID_Type id) {
	this->id = id;
}

TextureHandler::TextureHandler(GUID_Type id) {
	r.setID(id);
}

PublicTextureHandler TextureHandler::getReader() const {
	return r;
}

void TextureHandler::setID(GUID_Type id) {
	r.setID(id);
}

GUID_Type TextureHandler::getID() {
	return r.getID();
}

int main(int argc, char **argv) {
	TextureHandler handle(5);
	PublicTextureHandler pub_handle = handle.getReader();
	std::cout << pub_handle.getID() << std::endl;
	handle.setID(6);
	std::cout << handle.getID() << std::endl;
	std::cout << pub_handle.getID() << std::endl;
}



Pardon my bad C++ -- I haven't touched it for a year, so my consts and references might have some holes -- especially when it comes to pointers. boost:smart_pointer might be a better bet for you -- but I don't know what tools you have at your disposal. Either way, this seems pretty hackish to me.

 User Rating: 1657   |  Rate This User  Send Private MessageView ProfileView Journal Report this Post to a Moderator | Link

I just tested a release build using a pointer to the TextureHandler, setting a value, getting a PublicTextureHandler, and deleting the TextureHandler. I expected an access error. Reading from the PublicTextureHandler gives a value of 0. I don't know if this is defined behavior or not, maybe someone else can chime in. It seems rather odd.

Either way, you could just pass by value instead of reference and solve the issue.

 User Rating: 1657   |  Rate This User  Send Private MessageView ProfileView Journal Report this Post to a Moderator | Link

Ok well I was looking over your code, trying to see how it applies.
Does TextureHandler translate to my renderer? And PublicTextureHandler is equivalent to my TextureHandle?
If so then that is somewhat similar to one of ideas I proposed in my top post, which I do feel is a little hackish.

Quote:
The biggest issue is your requirement that the renderer can alter IDs.

Yes exactly, the renderer does of course need to be able to initalise them so that the client can use the handles to access per-resource services later on.
I equally would like handles to be re-usable, so that a single handle instance can be used to create-bind-destroy a resourse and then start all over again if need be.

As far as your trial run and deleting the TextureHandler instance goes, that sounds like undefined behaviour to me, but you're unlikely to get any compiler warnings and you can easily be lucky enough not to get any access error.

 User Rating: 1695   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Why do you actually need an unsigned integer as a handle? Why can't you not just use a typed pointer? Wouldn't that solve all your problems?

 User Rating: 1158   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

A typed pointer to what?
The underlying instance could be anything.

As far as i'm aware there are two main method to achieve what i'm doing:

1) Handles (the handle/body idiom? maybe?)

2) Polymorphism
i.e.

class TextureResource { };

class TextureResource_D3D : public TextureResource { };

class TextureResource_OGL : public TextureResource { };

// Use:
TextureResource* myTexture;
renderer->createTexture( myTexture, creationParams );

The polymorphism approach is more typesafe but incurs overheads that handles can avoid.

Either way the client needs some way to interact with the implementation specific services like binding and destroying a resource. In the polymorphism approach this interaction is done through the TextureResource base class interface. In the handle approach this is all done through the render-device interface (much like the underlying API anyway).

 User Rating: 1695   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

I had a quick look at the Handle/Body idiom (google). Still have to look at the bridge patter (fortunately I have the GoF book).

For this case (supporting OGL and D3D; which is a major task) I would use polymorphism. I don't see what overhead that should cause over handles.

When you use polymorphism you can use pointers instead of integer GUID. This is type safe and also ensures uniqueness which you would have to ensure yourself if you used integers.
If you usd integers GUIDs you wouldn't know the type of the resource and you had to look it up somehow plus you need some way to resolve this id to the actual instance of the class (probably with a hash table -> THIS is slow compared to the polymorphism approach)

 User Rating: 1158   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

The way I was thinking about it is that with GUIDs the render-device is entirely responsible for all GPU resources and not its client.
But a hash-map is certainly not necessary.

Off the top of my head...

Using GUIDS
  • cheap to pass around
  • contents are easily and safely examined - useful for state sorting for example
  • type-safety can be achieved by wrapping in a class
  • contents are too easily dangerously modified
  • (re)serialisable
  • All API specific code is kept in one place/class
  • render-device owns all GPU resources
    • during a device-lost state the render-device can release and re-load all resources and the client need never know or care
    • all services are performed through the render-device (potential for optimisations)
    • most of the costly virtual methods calls are made only at initialisation and kept to a minimum at render-time.
    • Since the render-device is the only polymorphic class, if the platform can be known at compile-time (when targetting a specific platform for example) then virtual functions can be inlined (circumstances permitting) or atleast cost no more than a non-virtual call.
    • Potential for changing API on the fly! (same argument as during device-lost state)
    • Under OGL
      • GPU resources from the API are integers handles anyway and can be trivially cast directly into unsigned int handles

    • Under D3D
      • GPU resources from the API are class instances, the unsigned int handle is used for a simple array lookup - alternatively the device could cast a D3D-resource pointer into a handle-type (perhaps a handle should be an unsigned long in this case)

Using polymorphism
  • pointers are cheap to pass around
  • automatically typesafe
  • must not be tempted to delete a pointer
  • can be dangerously modified
  • harder to safely examine the address of a pointer - e.g. for state sorting
  • cannot be serialised
  • API specific code is distributed to many classes
  • expensive virtual calls during rendering usually need to be made all over the place
  • each resource needs to own a reference to its render-device - memory overhead, possibly even one virtual call to the resource makes another virtual call to the render-device.
  • client effectively 'owns' the resources not the render-device (though the device can still keep a local copy)
    • During a device-lost state the client must re-request all their resources - all resource-owners need to be notified
    • No chance of the render-device optimising resource use since it doesnt own them.
    • changing API on the fly is difficult at best
    • little chance of compile-time optimisations.


So I still think the handles are more efficient and actually easier to manage than a pointer since its ignorant to whatever the render-device decides to do with the underlying resources.

Edit: My original question is still open by the way, I'd still be intrigued to hear oppinions, though I am tempted to just use public integers wrapped in a type-safe class.

 User Rating: 1695   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Well, you have put quite a bit of thought into this. I do not agree to everything you claim but it doesn't matter.
As for wrapping the integer GUIDS into a class for type safety: I wouldn't do it! It smells like a horrible hack and it is the fault of the client if it passes the wrong numbers into the functions.

Edit: device-lost state -> This can only happen in DX9 right?

 User Rating: 1158   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

I'd be interested to know what points you dont fully agree with or why, I'm completely open-minded and constructive criticism or expression of an alternate oppinion is never a bad thing

Why is adding type-safety a hack? I would have thought that preventing accidental misuse is a good thing but if the user wanted to subvert this then that is definately their fault.
Surely I am just adding a positive restriction (typesafety) with no real negative drawbacks?

As far as I'm aware all D3D versions below D3D10 can lose their device.
This is a non-issue in opengl however.

 User Rating: 1695   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

GUIDS: contents are easily and safely examined - useful for state sorting for example
Using polymorphism: harder to safely examine the address of a pointer - e.g. for state sorting

I don't see why there is any difference. You can't use the GUID directly just like you can't use the pointer directly for sorting.

GUIDS: contents are too easily dangerously modified
Using polymorphism: can be dangerously modified

This is not your problem. If the client modifies it even if he isn't supposed to it is entirely his fault.

GUIDS: (re)serialisable
Using polymorphism: cannot be serialised

I don't know how you intend to serialize it but only serializing the GUID number won't cut it. And for a graphics API you probably don't need serialization anyway.
And if you use OpenGL shaders you can't simply store the returned GLuint because you can't use the same for the next time ...

GUIDS: All API specific code is kept in one place/class
Using polymorphism: API specific code is distributed to many classes

This isn't entirely true. Where you put your methods is not dependant on this choice. Take a look at DX9! The also have classes and I don't think that is bad.

GUIDS: all services are performed through the render-device

Yeah, then you get a fat interface which isn't good either.

Changing API on the fly: I wouldn't go there. It is a complete mess.

GUIDS:
Under D3D GPU resources from the API are class instances, the unsigned int handle is used for a simple array lookup - alternatively the device could cast a D3D-resource pointer into a handle-type (perhaps a handle should be an unsigned long in this case)

Ok, not a hash-map but an array lookup (potentially even slower)

Virtual methods: You claim they are slow. Sure, they are slower than a normal function call and probably cannot be inlined but it solely depends on how often you call these virtual methods and how expensive the method body is. Generally I would say virtual methods are no problem (even though I had to reprogram my own software rasterizer to not use virtual functions because 1 call per pixel with a small method body caused to much overhead)


About wrapping you GUID into a class for type safety:
You could forward declare a struct/class and make the handle a pointer to this struct (opaque pointer). You gain type safty and don't expose the internal GUID.
But still I don't really like your design. DX9 is well designed but DX10 is even better. Personally I know only OpenGL. It is good but has some quirks. Hopefully Longs Peak makes it better. I wonder how Ogre does all this as it also supports both, DX and GL.

 User Rating: 1158   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Firstly thankyou very much for your response

Quote:
Original post by Trenki
I don't see why there is any difference. You can't use the GUID directly just like you can't use the pointer directly for sorting.

You can use a GUIDs value for state sorting, a GUID can represent a state. A pointer can also be used for state sorting, but to actually examine its value you'd usually cast it to an integer value anyway and (as far as i'm aware) there is no guarentee that there exists an integer type large enough to hold a memory address (although an int is usually the same size as a pointer).

Quote:

This is not your problem. If the client modifies it even if he isn't supposed to it is entirely his fault.

Yes I think I agree here, rather than mess about trying to prevent the client write-access I just shouldnt worry.

Quote:

I don't know how you intend to serialize it but only serializing the GUID number won't cut it. And for a graphics API you probably don't need serialization anyway.

Sorry yes, I dont think I'm likely to want to serialise these GPU resource handles, it was more a generic point for using handles over pointers - a handle can be serialised provided the same handle is restored afterwards which is unlikely in my case so it was a poor point of mine.

Quote:

And if you use OpenGL shaders you can't simply store the returned GLuint because you can't use the same for the next time ...

Do OGL shaders return a different ID everytime then? Are they not like texture objects where you generate an ID (either yourself or using glGenTextures)?

Quote:

This isn't entirely true. Where you put your methods is not dependant on this choice. Take a look at DX9! The also have classes and I don't think that is bad.

True, however usually when using polymorphism to abstract the API the API dependant code is put in the implementation class and with one implementation class per resource per API then its not all kept in one place. In DX9 they do use classes but these classes are the API, its object oriented, the OGL framework is object oriented but it doesnt use classes because it a C library.

Quote:

Yeah, then you get a fat interface which isn't good either.

Usually I would agree that a fat interface is a bad thing however the interface is for a render-device, which is a fat API interface in itself. Splitting it across multiple classes doesnt reduce the size or complexity of this interface it just hides the concept of the render-device more and more.

Quote:
Changing API on the fly: I wouldn't go there. It is a complete mess.

Probably, but GUIDs seem to make it more tempting.
Iterate each existing resource
Grab its creationParameters
Create the resource under the other API
Assign the newly created resource to the same ID as before
Destroy the current render-device and replace it with the new one
Use as before, clients dont notice a difference and didnt have to participate


Quote:

Ok, not a hash-map but an array lookup (potentially even slower)

An array lookup slower than a hash-map lookup?
A hash-map lookup (implementation dependant ofcourse) requires a hashing routine followed by an array lookup followed by a linked list iteration or a repeat of the hash routine.
Or were you thinking of a worst-case O(N) linear search? I mean a cheap O(1) lookup.

Quote:

Virtual methods: You claim they are slow. Sure, they are slower than a normal function call and probably cannot be inlined but it solely depends on how often you call these virtual methods and how expensive the method body is. Generally I would say virtual methods are no problem (even though I had to reprogram my own software rasterizer to not use virtual functions because 1 call per pixel with a small method body caused to much overhead)

I know in reality that virtual calls are a fairly small cost compared to the computations performed elsewhere, just that in my performance critical render-loop I'd like to keep them down to a minimum and using polymorphic resources and renderer means more virtual calls than a GUID approach (at least the way I'd intend to use it, of course it does depend how you would employ each approach)

Quote:

About wrapping you GUID into a class for type safety:
You could forward declare a struct/class and make the handle a pointer to this struct (opaque pointer). You gain type safty and don't expose the internal GUID.
But still I don't really like your design.

Could you maybe explain this further?

Quote:

DX9 is well designed but DX10 is even better. Personally I know only OpenGL. It is good but has some quirks. Hopefully Longs Peak makes it better.

In D3D9/10 both have the concept of a render-device much like the one I'm trying to design, OGL can be thought to have a render-device only its interface are free functions (obviously since its a C library so no classes).

Quote:

I wonder how Ogre does all this as it also supports both, DX and GL.

Ogre has a render-device that has a bunch of methods (like my redner-device design) but it then uses polymorphism for each resource type. Each resource has to carry about a pointer to the render-device and often a virtual call to a resource then makes another virtual call to the render-device to do some of the work.

 User Rating: 1695   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Quote:

Quote:

And if you use OpenGL shaders you can't simply store the returned GLuint because you can't use the same for the next time ...

Do OGL shaders return a different ID everytime then? Are they not like texture objects where you generate an ID (either yourself or using glGenTextures)?

In OpenGL you can generate your own IDs for texture objects and maybe other objects like display lists as well even without having to use glGenTextures.
This is actually a bad design choice and will be fixed with Longs Peak. For shader objects you can't choose your own but you must use the one returned by glGenShader(). Nvidia seemed to return simple integers incrementing the returned id at every shader creation. ATI probaly returned a pointer as the numbers were more arbitraty.

Quote:

Quote:

Ok, not a hash-map but an array lookup (potentially even slower)

An array lookup slower than a hash-map lookup?
A hash-map lookup (implementation dependant ofcourse) requires a hashing routine followed by an array lookup followed by a linked list iteration or a repeat of the hash routine.
Or were you thinking of a worst-case O(N) linear search? I mean a cheap O(1) lookup.


You have a GUID and you need to map this to the instance of the object; right?
So how would you do that in O(1) with a simple array lookup?? How big is your array?


Quote:

Quote:

About wrapping you GUID into a class for type safety:
You could forward declare a struct/class and make the handle a pointer to this struct (opaque pointer). You gain type safty and don't expose the internal GUID.
But still I don't really like your design.

Could you maybe explain this further?


You can put a struct without a body in a header file. You can have pointers to this struct without requireing the body either. This is called an opaque pointer as it has a type but what is inside it is unknown. You could maybe use such a pointer as handle. (Google opaque pointer -> wikipedia)


 User Rating: 1158   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Quote:
Original post by Trenki
For shader objects you can't choose your own but you must use the one returned by glGenShader(). Nvidia seemed to return simple integers incrementing the returned id at every shader creation. ATI probaly returned a pointer as the numbers were more arbitraty.

Aha thanks for the info, i wasnt aware but its not actually a problem I'd just use the same implementation technique as I do for D3D resources (example coming up)

Quote:

Quote:

Ok, not a hash-map but an array lookup (potentially even slower)

An array lookup slower than a hash-map lookup?
A hash-map lookup (implementation dependant ofcourse) requires a hashing routine followed by an array lookup followed by a linked list iteration or a repeat of the hash routine.
Or were you thinking of a worst-case O(N) linear search? I mean a cheap O(1) lookup.


You have a GUID and you need to map this to the instance of the object; right?
So how would you do that in O(1) with a simple array lookup?? How big is your array?[/quote]
Yes I need a 1->1 mapping between GUID and object instance, an array provides exactly that (the size is equal to the number of resource instances created)
e.g.

// At initialisation
std::vector< LPDIRECT3DTEXTURE9 > textureResources( numTextures );

// Get a D3D texture resource from a GUID with an O(1) lookup
LPDIRECT3DTEXTURE9 pTex = textureResources[ textureGUID ];


Quote:
You can put a struct without a body in a header file. You can have pointers to this struct without requireing the body either. This is called an opaque pointer as it has a type but what is inside it is unknown. You could maybe use such a pointer as handle. (Google opaque pointer -> wikipedia)

Hmm I looked it up, its basically the pimpl idiom, I'm not convinced I can fully apply this, its more useful when the implementation needs to change but you dont want to have expensive compile times and need a lightweight object to pass about. In my case the implementation is polymorphic hidden behind the render-device its not quite the same I dont think.

 User Rating: 1695   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

There's one way to enforce type-safety.

class GUID
{
  inline int id() const { return m_id; }
protected:
  GUID( int id, int t_id ) : m_id(id) {};
private:
  int m_id;   // resource id
  int m_t_id; // type id
};

template < class T >
class Identifier { enum { Value = 0 }; }

template <> class Identifier< Resource > { enum { Value = 1 }; };
template <> class Identifier< Model > { enum { Value = 2 }; };


template < class T >
class TypedGUID
{
public:
  GUID( int id ) : GUID( id, Identifier<T>::Value ) {}
};

typedef TypedGUID< Texture > TextureGUID;
typedef TypedGUID< Model > ModelGUID;

class AdvancedTexture : public Texture {}

typedef TypedGUID< AdvancedTexture > ATextureGUID;







Then, when passing parameters around, you can type enforce what goes where:
getTexture( TextureGUID &guid );
getModel( ModelGUID &guid );
...

getATexture( ATextureGUID &guid );

// This should work for TextureGUID and ATexture GUID
// although I wouldn't bet on it right now without testing
getTexture( TextureGUID &guid );





This isn't fool-proof, it can still be subverted. This, due to reliance on templates is compile-time safe. It does however make some aspects more tedious. One is dynamic identification of the type, or better yet, resolving GUID from id - common with serialization (but not your problem).

While this makes it somewhat harder to store just GUIDs and pass them to the rendering, it follows the same restrictions as passing virtual classes. Passing just GUID (or m_id value) around is identical to void * pointer. Passing TypedGUID around is same as passing base class pointer around.

Such approach can have some benefits, while preserving compile-time safety, the benefits however will depend on what you're trying to do.


 User Rating: 1890   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

I was wondering about some sort of template magic.
However it looks like you set the m_id in the constructor of the handle, as is common when using handles the handle is instantiated by the client and initialised elsewhere (the renderer), how did you intend on allowing the renderer to modify the m_id member on an already created handle?

 User Rating: 1695   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Could you not define an interface class on your renderer? this would mean the Gui'ds could be accessed directly by the renderer but not by anything else if i have your implementation straight in my head

 User Rating: 1050   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Yes, this is what I meant by using making the renderer a friend of the handle class:

typedef unsigned int GUID_Type;

class Renderer; // Forward declaration

// The Handle class - permits the renderer to access its private members
class TextureHandle {
  friend Renderer;
public:
    GUID_Type id() { return m_id; }
private:
    GUID_Type m_id;
};

// The renderer base class
class Renderer {
public:
    /*
        pure virtual interface
    */

protected:
    void setTextureID (TextureHandle& handle, GUID_Type id);
};

void Renderer::setTextureID (TextureHandle& handle, GUID_Type id) {
    handle.m_id = id;
}

// Implementation of a renderer
class SomeRenderer : public Renderer;
{
    // Can use setTextureID() to initialise a texture handle
    // since it doesnt have direct access to TextureHandle.m_id
};




I see this example as: "Using the keyword friend to extend the interface of TextureHandle into the class Renderer so that only derived classes may benefit from the complete handle interface and clients may only see a limited handle interface."

It sounds clean, but is it good design to use friends? And is this just over-complicating things? Is it fully justified to limit the interface to clients? Does a handle even need an interface?

 User Rating: 1695   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Quote:
Original post by dmatter
Quote:
Quote:

Quote:
Original post by Trenki
Ok, not a hash-map but an array lookup (potentially even slower)

An array lookup slower than a hash-map lookup?
A hash-map lookup (implementation dependant ofcourse) requires a hashing routine followed by an array lookup followed by a linked list iteration or a repeat of the hash routine.
Or were you thinking of a worst-case O(N) linear search? I mean a cheap O(1) lookup.

You have a GUID and you need to map this to the instance of the object; right?
So how would you do that in O(1) with a simple array lookup?? How big is your array?

Yes I need a 1->1 mapping between GUID and object instance, an array provides exactly that (the size is equal to the number of resource instances created)
e.g.

// At initialisation
std::vector< LPDIRECT3DTEXTURE9 > textureResources( numTextures );

// Get a D3D texture resource from a GUID with an O(1) lookup
LPDIRECT3DTEXTURE9 pTex = textureResources[ textureGUID ];


And what happens when the user destroys/releases the first resource? Do you leave the array as is and re-use the slot? Or do you actually remove the item from the array, which would be extremely slow, and would invalidate all the other GUIDS.

If you really can't bring yourself to pass pointers around (and honestly, I don't think your reasoning not to is very compelling), how about stashing the resources in a std::list and using std::list::iterators as your GUIDs? (it isn't perfect, but better than GUIDs indexing a huge array).

But again, why not use pointers? All you are doing is adding in the cost of a table lookup to retrieve the pointer. You might as well just use the pointer directly.

 User Rating: 1744   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Quote:
Original post by swiftcoder
And what happens when the user destroys/releases the first resource? Do you leave the array as is and re-use the slot? Or do you actually remove the item from the array, which would be extremely slow, and would invalidate all the other GUIDS.

Nah the slot is re-used or ignored.. actually it doesnt matter since this is an implementation detail that is abstracted away, the implementation is in a DLL and can be changed willy-nilly.

Quote:

If you really can't bring yourself to pass pointers around (and honestly, I don't think your reasoning not to is very compelling), how about stashing the resources in a std::list and using std::list::iterators as your GUIDs? (it isn't perfect, but better than GUIDs indexing a huge array).

Again, the specific renderer implementation is reponsible for managing this (or it could delegate the task), whether it uses a list, an array, simple casts from OGL handles to integer handles.. its all taken care of under the hood, if the implementation changes the handles dont so nothing breaks.

Quote:

But again, why not use pointers? All you are doing is adding in the cost of a table lookup to retrieve the pointer. You might as well just use the pointer directly.

void pointers? Or you mean using polymorphism and wrapping API specific resources?

void pointers are no more typesafe than using handles (and infact the implementation might choose to (somewhat riskily) cast a pointer into a handle so theyre almost synonymous with each other.)

using polymorphism requires virtual calls, which typically require function pointers behind the scenes that are looked up from a vtable anyway, so actually in this respect a table lookup is not a greater overhead than a virtual call.. both fairly minimal costs but a virtual call is always going to cost the same, with handles the cost can be minimised further depending again on the actual implementation.

 User Rating: 1695   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

For my own sanity I thought Id look whether there are any other cases where handles are used over per-resource polymorphic classes for renderer design, without really knowing what I'm looking for its quite hard but Ive found 3 examples worth a mention:

GPGems - A Generic Handle-Based Resource Manager

Yann on generating texture GUIDs for disk loading resources
Yann uses 32-bit GUIDs for GPU resource handles

 User Rating: 1695   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Quote:
Original post by dmatter
how did you intend on allowing the renderer to modify the m_id member on an already created handle?


You don't.

In the same way that you can't convert Resource * into Model * if the two types are unrelated.

When a resource is created, you associate it with TypedGUID. After a TypedGUID has been created, it's there. You can't change it anymore, since then it's not an id anymore. The only way to change it is to destroy the resource, and re-create it, in the same way databases enforce unique keys - once you remove a row, you'll re-create it with new ID.

The system is somewhat safe, since your API uses TypedGUID to access resources, which automatically assigns proper ID. If someone derives the GUID class, they won't be able to pass it into your API. And if someone derives TypedGUID for evil purposes, you can always make a test to see if they called your original constructor.


 User Rating: 1890   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

But in your example you'd have to pass the handle to the renderer:

getATexture( ATextureGUID &guid );

that implies to me a usage like this:

ATextureGUID texGUID; // default constructor?
getATexture( texGUID );


but you seem to be talking about a different type of usage?
In the example above the getATexture function would need to modify the already constructed class wouldnt it?

 User Rating: 1695   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Quote:
Original post by dmatter
Quote:
Original post by swiftcoder
And what happens when the user destroys/releases the first resource? Do you leave the array as is and re-use the slot? Or do you actually remove the item from the array, which would be extremely slow, and would invalidate all the other GUIDS.

Nah the slot is re-used or ignored.. actually it doesnt matter since this is an implementation detail that is abstracted away, the implementation is in a DLL and can be changed willy-nilly.

So basically you are making a memory/performance tradeoff. Fine in a desktop environment, but remember that other systems may impose much tighter memory budgets.

Quote:
Quote:

If you really can't bring yourself to pass pointers around (and honestly, I don't think your reasoning not to is very compelling), how about stashing the resources in a std::list and using std::list::iterators as your GUIDs? (it isn't perfect, but better than GUIDs indexing a huge array).

Again, the specific renderer implementation is reponsible for managing this (or it could delegate the task), whether it uses a list, an array, simple casts from OGL handles to integer handles.. its all taken care of under the hood, if the implementation changes the handles dont so nothing breaks.

What possible reason could there be to change implementations (I take it you are talking underlying render APIs here) while running? Most modern games don't even let you switch in and out of fullscreen in a reasonable manner, let alone change from GL to D3D at runtime! Plus this is only really useful on Windows (maybe your only target platform?) as most other systems/OSs only have a single 3D API.

Quote:
Quote:

But again, why not use pointers? All you are doing is adding in the cost of a table lookup to retrieve the pointer. You might as well just use the pointer directly.

void pointers? Or you mean using polymorphism and wrapping API specific resources?

void pointers are no more typesafe than using handles (and infact the implementation might choose to (somewhat riskily) cast a pointer into a handle so theyre almost synonymous with each other.)

using polymorphism requires virtual calls, which typically require function pointers behind the scenes that are looked up from a vtable anyway, so actually in this respect a table lookup is not a greater overhead than a virtual call.. both fairly minimal costs but a virtual call is always going to cost the same, with handles the cost can be minimised further depending again on the actual implementation.

Void pointers don't seem to help you anyway, since you can't guarantee that a new class will be allocated at the same address anyway (well, not without some memory manager voodoo at any rate).

Quite frankly, virtual calls are not so slow! And I can guarantee that there will be *some* virtual calls somewhere in the system anyway. Have you any proof that virtual calls are a significant bottleneck in your engine? Otherwise we can chalk this down as premature optimisation.

And anyway, GUID handles aren't going to reduce virtual function calls all by themselves. I mean, what is going to be returned from that table lookup? A class right? And then either that class has virtual methods to delegate stuff, or you have a great big switch statement to cover all possible cases - which is dubiously faster and much messier.

My main point here though, is that you don't *need* any virtual calls on the implementation side to start with. Okay, so when the user sets a texture property through a pointer to 'ITexture', it uses a virtual call to delegate to the right subclass... But when it is time to render, the Renderer already knows which subclass it is dealing with (because the renderer created it), so it just casts 'ITexture' to 'D3DTexture', or whatever, and retrieves the API-specific handle from a non-polymorphic function/field. Maybe OO purists won't like that approach, but they will probably like it better than tossing non-typesafe handles through user code.

 User Rating: 1744   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link
Page:   1 2 »»
All times are ET (US)

Post Reply
 Last Thread Next Thread 
Forum Rules:
You may not post new threads
You may post replies
You may not edit your posts
You may not use HTML in your posts
Jump To:
Administrative Options: