I'm building my own pet engine and after struggling with how to handle resource ownership for a while, I came across this question and specifically Josh and Sean's responses (as well as Sean's blog on shared_ptr), which really made a whole lot of sense to me. So I went ahead and implemented my own similar system, and so far it's working great except for one minor snag - what do I do when I need to access a resource of a derived type when the handle only refers to the base type?
Say for instance I have a Material class that keeps a list of handles to textures it needs for rendering:
class Material
{
public:
void SetTexture(int textureSlot, const Handle<Texture>& texture);
private:
SomeContainer<Handle<Texture>> m_textures;
};
Then I grab a handle to a texture from my texture library, set it to the material, and everything works great:
Handle<Texture> fnorbTexture = TextureLibary::GetResource("fnorb.tga");
fnorbMaterial->SetTexture(0, fnorbTexture); // fnorbtastic!
But what happens now when I have a RenderTarget class that derives from Texture?
class GraphicsSystem
{
public:
void SetRenderTarget(int slot, Handle<RenderTarget> renderTarg);
// Other stuff
};
Handle<Texture> renderTarg =
TextureLibrary::PleaseGiveMeARenderTarget(width, height, flags);
// ERROR: Can't cast Handle<Texture> to Handle<RenderTarget>!
graphicsSystem->SetRenderTarget(0, renderTarg);
// ... Render stuff to render target ...
// Set the current render target back to the standard frame buffer
graphicsSystem->SetRenderTarget(0, kNullHandle);
// Now use the render target as a texture
superAwesomeBlorgomaticPostEffectMaterial->SetTexture(0, renderTarg); // OK!
Or maybe:
Handle<RenderTarget> renderTarg =
RenderTargetLibrary::PleaseGiveMeARenderTarget(width, height, flags);
graphicsSystem->SetRenderTarget(0, renderTarg); // OK!
// ... Render stuff to render target ...
// Set the current render target back to the standard frame buffer
graphicsSystem->SetRenderTarget(0, kNullHandle);
// Now use the render target as a texture
// ERROR: Can't cast Handle<RenderTarget> to Handle<Texture>!
superAwesomeBlorgomaticPostEffectMaterial->SetTexture(0, renderTarg);
Possible solutions, and my thoughts on each:
Borrow a render target resource as a Handle<RenderTarget>, and in Material::SetTexture(), pass it using reinterpret_cast<const Handle<Texture>&>.
- Compiles and probably works, but looks ugly and I feel dirty afterwards.
Or: Borrow a render target resource as a Handle<Texture>, and rewrite SetRenderTarget to take a const Handle<Texture>& instead. Then:
When I dereference the handle, use
dynamic_castto cast the pointer to aRenderTarget*as necessary.- RTTI is "Bad" TM, which is to say that it very often gets disabled (along with C++ exceptions, etc.) in game applications due to unnecessary overhead, particularly on consoles. Plus resorting to
dynamic_castfeels like I'm "doing it wrong," architecturally speaking.
- RTTI is "Bad" TM, which is to say that it very often gets disabled (along with C++ exceptions, etc.) in game applications due to unnecessary overhead, particularly on consoles. Plus resorting to
I know it's a
RenderTarget; it was when I created it, and if it weren't, I wouldn't be passing it toSetRenderTargetin the first place!reinterpret_cast!- Do I REALLY know? If I'm wrong, then I'm casting something else to something it's not, and I can't even check if it's
nullptrif I'm wrong. Also, again, I'd like to avoid casts if possible. But I don't entirely disagree with the logic here...
- Do I REALLY know? If I'm wrong, then I'm casting something else to something it's not, and I can't even check if it's
Implement a lightweight homegrown alternative to RTTI, and use that instead.
- I've seen this done in one or two engines, but it sounds like some fairly heavy-duty wheel-reinventing...
Don't derive
RenderTargetfromTexture; instead, add a flag that says whether aTextureis aRenderTargetor not. If it's not, thenSetRenderTargetwill simply fail.- Potentially lots of excess baggage added to the
Textureclass. (What's another pointer to anID3D11RenderTargetViewamong friends?) Doesn't sound like it scales very well.
- Potentially lots of excess baggage added to the
Derive
RenderTargetfromTexture, but add a virtual method toTexturecalledGetRenderTarget()that normally returnsnullptrbut is overridden inRenderTargetto returnthis. Similar to the flag method above, but without the excess baggage.- Feels like a poor man's
dynamic_cast. Again, doesn't scale well (add a new method for each derived class that needs it?). But if myRenderTargetscenario is maybe the only case I actually need something like this, I'm beginning to feel like maybe this solution isn't so bad...
- Feels like a poor man's
Or maybe: Add another SetTexture() method to Material that takes a const Handle<RenderTarget>& instead?
- Again, scaleability. And this almost certainly complicates the implementation...
Or finally: Somehow make a handle to a derived type automagically compatible with a handle to its base type (without say, deriving Handle<RenderTarget> from Handle<Texture>)? I feel like theoretically this might be possible, but I can't seem to wrap my head around it at the moment. Template metaprogramming magic?
Wow, this got a lot longer than I thought it would. Is there a better solution that I'm just missing? Or is one of the above a good enough answer? Feel free to tear apart my arguments if they're flawed. I feel like I'm vastly overthinking this...