• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
vinnyvicious

Using C++11 smart pointers for asset manager

23 posts in this topic

So, i'm working on a small asset manager for my engine, and i'm using type inference and smart pointers from C++ to add the assets to an unordered_map. I add a string and the smart pointer to this set:

assets.insert(make_pair('my-bitmap', make_shared<Bitmap>(100,100)));

This assets is a member of the AssetsManager, which it's single instance is stored in a member of the Scene object. Is that a good practice? Is this more efficient than another solution, since i don't have to work on pointers manually? Also, what happens when i remove the Scene object?

0

Share this post


Link to post
Share on other sites


Is that a good practice?

It seems OK to me.

 


Is this more efficient than another solution, since i don't have to work on pointers manually?

 

No. Why would it more efficient? The point of smart pointers is that it is easier to get things right. It actually generally sacrifices a bit of efficiency in the process. But that's OK.

 


Also, what happens when i remove the Scene object?

 

The shared pointer gets destroyed, which means that the reference counter will be decremented. If it reaches 0 (which means no one else is using this resource), the Bitmap will be destroyed and its memory released.

1

Share this post


Link to post
Share on other sites

Why are you using shared_ptr?

To me it seems "natural" that an asset manager "owns" the assets exclusively. It seems to be strange that an asset can outlive the manager, so no shared ownership should be required.

Having said this, unique_ptr is the more obvious solution to your problem.

Do not use shared_ptr unless you really need shared ownership, especially if efficiency is your concern.

I disagree.

 

Having a shared_ptr in asset manager and handing out weak_ptr to asset users is a solid strategy.

1

Share this post


Link to post
Share on other sites

[Thats reply to cdoubleplusgood's post, in the meantime someone posted so just want to clarify as I didn't quote the original post ;)]

 

Question is - whats his plan to retrieve assets from manager? If he uses unique_ptr, he shouldn't really store any reference/pointer so he has to query manager every time he uses an asset, which will probably be every frame. He can't really pre-fetch assets that he uses and release them when he's done.

 

Also, when using shared_ptr manager knows when its the only owner left and can release resource (or keep it for some time before releasing completly). 

 

How would unique_ptr be used in such pipeline?

Edited by noizex
0

Share this post


Link to post
Share on other sites

Having a shared_ptr in asset manager and handing out weak_ptr to asset users is a solid strategy.

I don't understand this strategy. Why weak_ptr? The users will have to lock and check for expiration everytime using the asset. If you already have shared_ptr in the manager, why not for the users?

0

Share this post


Link to post
Share on other sites

[Thats reply to cdoubleplusgood's post, in the meantime someone posted so just want to clarify as I didn't quote the original post ;)]

 

Question is - whats his plan to retrieve assets from manager? If he uses unique_ptr, he shouldn't really store any reference/pointer so he has to query manager every time he uses an asset, which will probably be every frame. He can't really pre-fetch assets that he uses and release them when he's done.

 

Also, when using shared_ptr manager knows when its the only owner left and can release resource (or keep it for some time before releasing completly). 

 

How would unique_ptr be used in such pipeline?

 

I don't know what the OP's manager actually does and what he wants to achieve. Of course, using shared_ptr for automatic resource management can be very convenient. However, he asked for efficiency, and shared_ptr is not the most efficient way to maintain pointers, especially because shared_ptr is thread safe even if you don't need it: The reference counters are always synchronized.

0

Share this post


Link to post
Share on other sites

 

Having a shared_ptr in asset manager and handing out weak_ptr to asset users is a solid strategy.

I don't understand this strategy. Why weak_ptr? The users will have to lock and check for expiration everytime using the asset. If you already have shared_ptr in the manager, why not for the users?

 

Because they're users. Giving out only weak_ptr's opens up the possibility to, f.ex. unload some resources that aren't actually being used in favor of some other high priority resources. This would need some mechnanism to avoid thrashing, tho. If you used shared_ptr instead, you wouldn't have the possibility to track actual usage.

1

Share this post


Link to post
Share on other sites

Of course, using shared_ptr for automatic resource management can be very convenient. However, he asked for efficiency, and shared_ptr is not the most efficient way to maintain pointers, especially because shared_ptr is thread safe even if you don't need it: The reference counters are always synchronized.
This is certainly correct, and something to always keep in mind when copying around shared pointers. Passing around a shared or weak pointer is not close to a no-op as compared to passing around a raw pointer.

 

However, in the context of an asset manager you need to consider that the rather slow atomic increments/decrements used in the shared pointer are entirely neglegible compared to only a single disk access for loading an asset. We're talking of 30-50 nanoseconds versus 8-10 milliseconds.

 

Formally, giving out a weak pointer as suggested by Mnemotic is the correct thing to do (though giving out shared pointers would probably work just fine 90% of the time too), seeing how the manager owns the resources and should be allowed to toss them on a as-needed base, but safely so no resources "disappear" while in use.

1

Share this post


Link to post
Share on other sites

I can't see how a solution that passes weak pointers would work in the case of multi-threaded code. In single-threaded code, I can check if my weak pointer is still valid before I use it, and I know that the object is not going away while I am using it. How do I get such a guarantee in multi-threaded code?

 

You're locking weak_ptr before use, which creates shared_ptr<> temporarily, so you have guarantee that it won't go away in that scope. Thats my understanding at least.

0

Share this post


Link to post
Share on other sites

[Thats reply to cdoubleplusgood's post, in the meantime someone posted so just want to clarify as I didn't quote the original post ;)]

 

Question is - whats his plan to retrieve assets from manager? If he uses unique_ptr, he shouldn't really store any reference/pointer so he has to query manager every time he uses an asset, which will probably be every frame. He can't really pre-fetch assets that he uses and release them when he's done.

 

Also, when using shared_ptr manager knows when its the only owner left and can release resource (or keep it for some time before releasing completly). 

 

How would unique_ptr be used in such pipeline?

Supposedly he would then be handing out raw pointers from the manager. Which is fine, it's usage of new/delete that's discouraged, not raw pointers.

0

Share this post


Link to post
Share on other sites


You're locking weak_ptr before use, which creates shared_ptr<> temporarily, so you have guarantee that it won't go away in that scope. Thats my understanding at least.

Yes. You create a shared_ptr from the weak_ptr using lock, then you test if the shared_ptr is !nullptr.

0

Share this post


Link to post
Share on other sites

Just giving some info to the discussion: I'm storing SDL_Texture's from calls made to IMG_Load and SDL_CreateTextureFromSurface subsequently. I fetch the textures from there to render with SDL_RenderCopyEx. I'm also storing Mix_Chunk and Mix_Music.

0

Share this post


Link to post
Share on other sites

I find it a bit curious he used the phrasing "small asset manager" and suddenly we're all rambling about efficiency and what is pragmatically correct and it being multi-threaded and.. goodness do we even know if he cares about any of this stuff?

 

To be perfectly blunt performance loss from the internals of your asset manager are probably going to make an almost completely negligible effect on performance, the only important thing here is that you're not reloading a resource from disk unless you absolutely have to, how that works entirely depends on your game. For some games it makes sense to stream-load resources and unload a lot, for others you might just be sticking a bunch of junk in at the start of a level or something.

 

In the end even the naive approach of just loading everything at the start works fine for a great majority of games. Whats most important is you figure out what the thing needs to do to be easily usable from the outside.

0

Share this post


Link to post
Share on other sites

Actually, i care about all those concerns. I might not implement strategy A or strategy B, but i would like to know them both. While my current project does not plan to multi-threaded, i would love to hear the opinion of more experienced developers towards my code and which challenges i might face with it. I'm enjoying the discussion, thanks everyone for all the insights! wink.png

0

Share this post


Link to post
Share on other sites

I think I would start with shared pointers: It looks like it's the easiest solution to get right, and it should cover most situations. It's good to know about weak_ptr<T>::lock(), but it seems cumbersome.

0

Share this post


Link to post
Share on other sites

The constructor of shared_ptr allows you to supply a Deleter, a function function object (that is, a class which has an operator() taking a pointer) which it will use to delete the object instead of calling delete.

 

Usually, this is used for using your own allocator, but nothing prevents you from putting any other code (such as SDL_FreeTexture) followed by ::delete into that function. I wouldn't do it personally (I'd rather call SDL_FreeTexture in the texture object's destructor because it makes me sleep more comfortably), but it is certainly possible to do either one.

Edited by samoth
0

Share this post


Link to post
Share on other sites

The constructor of shared_ptr allows you to supply a Deleter, a function function object (that is, a class which has an operator() taking a pointer) which it will use to delete the object instead of calling delete.


That's not completely correct. The deleter must be a functor, that is if 'd' is an instance of Deleter then 'd(pointer)' must be a valid expression. So Deleter can be a normal function pointer type, a function object or even a lambda expression.
0

Share this post


Link to post
Share on other sites

While we are talking about the deleter, here's one thing that surprised me in the past: The default deleter calls delete on the pointer provided to the constructor, using the type passed to the constructor, not the type specified in the shared_ptr template. This means that, even if you don't make the destructor of your base class virtual, shared_ptr can end up calling the correct destructor!

 

#include <iostream>
#include <memory>

struct Base {
  int x;
 
  Base(int x) : x(x) {
  }
 
  // Look, ma! No virtual destructor!
};

struct Derived : Base {
  Derived(int x) : Base(x) {
  }
 
  ~Derived() {
    std::cout << "A Derived with x=" << x << " is being destructed. Magic!\n";
  }
};
                                                                                                                                  
std::shared_ptr<Base> factory(int x) {                                                                                            
  return std::shared_ptr<Base>(new Derived(x));                                                                                   
}                                                                                                                                 
                                                                                                                                  
int main() {                                                                                                                      
  std::shared_ptr<Base> pointer = factory(4);                                                                                     
  std::cout << "So now I have a pointer to Base with x=" << pointer->x << '\n';                                                   
}                                                                                                                                 
0

Share this post


Link to post
Share on other sites
Yes, that is an interesting property which was present even in the good old boost::shared_ptr. Actually, there was even a rather annoying bug with that in several 1.3x Boost versions: manually constructing the boost::shared_ptr out of the pointer had that behavior, but using boost::make_shared didn't. That was a source of a lot of fun under the right circumstances and a great sigh of relief throughout the universe when it was fixed.
0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0