Files
entt/md_docs_md_resource.html
2021-12-21 13:20:51 +01:00

180 lines
16 KiB
HTML

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.9.1"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>EnTT: Crash Course: resource management</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">EnTT
&#160;<span id="projectnumber">3.9.0</span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.9.1 -->
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&amp;dn=gpl-2.0.txt GPL-v2 */
var searchBox = new SearchBox("searchBox", "search",false,'Search','.html');
/* @license-end */
</script>
<script type="text/javascript" src="menudata.js"></script>
<script type="text/javascript" src="menu.js"></script>
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&amp;dn=gpl-2.0.txt GPL-v2 */
$(function() {
initMenu('',true,false,'search.php','Search');
$(document).ready(function() { init_search(); });
});
/* @license-end */</script>
<div id="main-nav"></div>
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>
<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0"
name="MSearchResults" id="MSearchResults">
</iframe>
</div>
</div><!-- top -->
<div class="PageDoc"><div class="header">
<div class="headertitle">
<div class="title">Crash Course: resource management </div> </div>
</div><!--header-->
<div class="contents">
<div class="textblock"><h1><a class="anchor" id="autotoc_md148"></a>
Introduction</h1>
<p>Resource management is usually one of the most critical part of a software like a game. Solutions are often tuned to the particular application. There exist several approaches and all of them are perfectly fine as long as they fit the requirements of the piece of software in which they are used.<br />
Examples are loading everything on start, loading on request, predictive loading, and so on.</p>
<p><code>EnTT</code> doesn't pretend to offer a <em>one-fits-all</em> solution for the different cases. Instead, it offers a minimal and perhaps trivial cache that can be useful most of the time during prototyping and sometimes even in a production environments.<br />
For those interested in the subject, the plan is to improve it considerably over time in terms of performance, memory usage and functionalities. Hoping to make it, of course, one step at a time.</p>
<h1><a class="anchor" id="autotoc_md149"></a>
The resource, the loader and the cache</h1>
<p>There are three main actors in the model: the resource, the loader and the cache.</p>
<p>The <em>resource</em> is whatever users want it to be. An image, a video, an audio, whatever. There are no limits.<br />
As a minimal example:</p>
<div class="fragment"><div class="line"><span class="keyword">struct </span>my_resource { <span class="keyword">const</span> <span class="keywordtype">int</span> value; };</div>
</div><!-- fragment --><p>A <em>loader</em> is a class the aim of which is to load a specific resource. It has to inherit directly from a dedicated base class as in the following example:</p>
<div class="fragment"><div class="line"><span class="keyword">struct </span>my_loader final: <a class="code" href="classentt_1_1resource__loader.html">entt::resource_loader</a>&lt;my_loader, my_resource&gt; {</div>
<div class="line"> <span class="comment">// ...</span></div>
<div class="line">};</div>
<div class="ttc" id="aclassentt_1_1resource__loader_html"><div class="ttname"><a href="classentt_1_1resource__loader.html">entt::resource_loader</a></div><div class="ttdoc">Base class for resource loaders.</div><div class="ttdef"><b>Definition:</b> <a href="loader_8hpp_source.html#l00042">loader.hpp:42</a></div></div>
</div><!-- fragment --><p>Where <code>my_resource</code> is the type of resources it creates.<br />
A resource loader must also expose a public const member function named <code>load</code> that accepts a variable number of arguments and returns a shared pointer to a resource.<br />
As an example:</p>
<div class="fragment"><div class="line"><span class="keyword">struct </span>my_loader: <a class="code" href="classentt_1_1resource__loader.html">entt::resource_loader</a>&lt;my_loader, my_resource&gt; {</div>
<div class="line"> std::shared_ptr&lt;my_resource&gt; load(<span class="keywordtype">int</span> value)<span class="keyword"> const </span>{</div>
<div class="line"> <span class="comment">// ...</span></div>
<div class="line"> <span class="keywordflow">return</span> std::shared_ptr&lt;my_resource&gt;(<span class="keyword">new</span> my_resource{ value });</div>
<div class="line"> }</div>
<div class="line">};</div>
</div><!-- fragment --><p>In general, resource loaders should not have a state or retain data of any type. They should let the cache manage their resources instead.<br />
As a side note, base class and CRTP idiom aren't strictly required with the current implementation. One could argue that a cache can easily work with loaders of any type. However, future changes won't be breaking ones by forcing the use of a base class today and that's why the model is already in its place.</p>
<p>Finally, a cache is a specialization of a class template tailored to a specific resource:</p>
<div class="fragment"><div class="line"><span class="keyword">using</span> my_cache = <a class="code" href="classentt_1_1resource__cache.html">entt::resource_cache&lt;my_resource&gt;</a>;</div>
<div class="line"> </div>
<div class="line"><span class="comment">// ...</span></div>
<div class="line"> </div>
<div class="line">my_cache cache{};</div>
<div class="ttc" id="aclassentt_1_1resource__cache_html"><div class="ttname"><a href="classentt_1_1resource__cache.html">entt::resource_cache</a></div><div class="ttdoc">Simple cache for resources of a given type.</div><div class="ttdef"><b>Definition:</b> <a href="cache_8hpp_source.html#l00027">cache.hpp:27</a></div></div>
</div><!-- fragment --><p>The idea is to create different caches for different types of resources and to manage each one independently in the most appropriate way.<br />
As a (very) trivial example, audio tracks can survive in most of the scenes of an application while meshes can be associated with a single scene and then discarded when users leave it.</p>
<p>A cache offers a set of basic functionalities to query its internal state and to <em>organize</em> it:</p>
<div class="fragment"><div class="line"><span class="comment">// gets the number of resources managed by a cache</span></div>
<div class="line"><span class="keyword">const</span> <span class="keyword">auto</span> size = cache.size();</div>
<div class="line"> </div>
<div class="line"><span class="comment">// checks if a cache contains at least a valid resource</span></div>
<div class="line"><span class="keyword">const</span> <span class="keyword">auto</span> empty = cache.empty();</div>
<div class="line"> </div>
<div class="line"><span class="comment">// clears a cache and discards its content</span></div>
<div class="line">cache.clear();</div>
</div><!-- fragment --><p>Besides these member functions, a cache contains what is needed to load, use and discard resources of the given type.<br />
Before exploring this part of the interface, it makes sense to mention how resources are identified. They have type <code>id_type</code> and therefore they can be created explicitly as in the following example:</p>
<div class="fragment"><div class="line">constexpr <span class="keyword">auto</span> identifier = <span class="stringliteral">&quot;my/resource/identifier&quot;</span>_hs;</div>
<div class="line"><span class="comment">// this is equivalent to the following</span></div>
<div class="line">constexpr <a class="code" href="namespaceentt.html#a13e040e7b38a8f86d1ab2f096f37b627">entt::id_type</a> hs = <a class="code" href="classentt_1_1basic__hashed__string.html">entt::hashed_string</a>{<span class="stringliteral">&quot;my/resource/identifier&quot;</span>};</div>
<div class="ttc" id="aclassentt_1_1basic__hashed__string_html"><div class="ttname"><a href="classentt_1_1basic__hashed__string.html">entt::basic_hashed_string</a></div><div class="ttdoc">Zero overhead unique identifier.</div><div class="ttdef"><b>Definition:</b> <a href="hashed__string_8hpp_source.html#l00058">hashed_string.hpp:58</a></div></div>
<div class="ttc" id="anamespaceentt_html_a13e040e7b38a8f86d1ab2f096f37b627"><div class="ttname"><a href="namespaceentt.html#a13e040e7b38a8f86d1ab2f096f37b627">entt::id_type</a></div><div class="ttdeci">std::uint32_t id_type</div><div class="ttdoc">Alias declaration for type identifiers.</div><div class="ttdef"><b>Definition:</b> <a href="core_2fwd_8hpp_source.html#l00013">fwd.hpp:13</a></div></div>
</div><!-- fragment --><p>The class <code>hashed_string</code> is described in a dedicated section, so I won't go in details here.</p>
<p>Resources are loaded and thus stored in a cache through the <code>load</code> member function. It accepts the loader to use as a template parameter, the resource identifier and the parameters used to construct the resource as arguments:</p>
<div class="fragment"><div class="line"><span class="comment">// uses the identifier declared above</span></div>
<div class="line">cache.load&lt;my_loader&gt;(identifier, 0);</div>
<div class="line"> </div>
<div class="line"><span class="comment">// uses a hashed string directly</span></div>
<div class="line">cache.load&lt;my_loader&gt;(<span class="stringliteral">&quot;another/identifier&quot;</span>_hs, 42);</div>
</div><!-- fragment --><p>The function returns a handle to the resource, whether it already exists or is loaded. In case the loader returns an invalid pointer, the handle is invalid as well and therefore it can be easily used with an <code>if</code> statement:</p>
<div class="fragment"><div class="line"><span class="keywordflow">if</span>(<a class="code" href="classentt_1_1resource__handle.html">entt::resource_handle</a> handle = cache.load&lt;my_loader&gt;(<span class="stringliteral">&quot;another/identifier&quot;</span>_hs, 42); handle) {</div>
<div class="line"> <span class="comment">// ...</span></div>
<div class="line">}</div>
<div class="ttc" id="aclassentt_1_1resource__handle_html"><div class="ttname"><a href="classentt_1_1resource__handle.html">entt::resource_handle</a></div><div class="ttdoc">Shared resource handle.</div><div class="ttdef"><b>Definition:</b> <a href="resource_2handle_8hpp_source.html#l00025">handle.hpp:25</a></div></div>
</div><!-- fragment --><p>Before trying to load a resource, the <code>contains</code> member function can be used to know if a cache already contains a specific resource:</p>
<div class="fragment"><div class="line"><span class="keyword">auto</span> exists = cache.contains(<span class="stringliteral">&quot;my/identifier&quot;</span>_hs);</div>
</div><!-- fragment --><p>There exists also a member function to use to force a reload of an already existing resource if needed:</p>
<div class="fragment"><div class="line"><span class="keyword">auto</span> <a class="code" href="namespaceentt.html#a5a0027b922894421f703102c374c234c">handle</a> = cache.reload&lt;my_loader&gt;(<span class="stringliteral">&quot;another/identifier&quot;</span>_hs, 42);</div>
<div class="ttc" id="anamespaceentt_html_a5a0027b922894421f703102c374c234c"><div class="ttname"><a href="namespaceentt.html#a5a0027b922894421f703102c374c234c">entt::handle</a></div><div class="ttdeci">basic_handle&lt; entity &gt; handle</div><div class="ttdoc">Alias declaration for the most common use case.</div><div class="ttdef"><b>Definition:</b> <a href="entity_2fwd_8hpp_source.html#l00069">fwd.hpp:69</a></div></div>
</div><!-- fragment --><p>As above, the function returns a handle to the resource that is invalid in case of errors. The <code>reload</code> member function is a kind of alias of the following snippet:</p>
<div class="fragment"><div class="line">cache.discard(identifier);</div>
<div class="line">cache.load&lt;my_loader&gt;(identifier, 42);</div>
</div><!-- fragment --><p>Where the <code>discard</code> member function is used to get rid of a resource if loaded. In case the cache doesn't contain a resource for the given identifier, <code>discard</code> does nothing and returns immediately.</p>
<p>So far, so good. Resources are finally loaded and stored within the cache.<br />
They are returned to users in the form of handles. To get one of them later on:</p>
<div class="fragment"><div class="line"><span class="keyword">auto</span> <a class="code" href="namespaceentt.html#a5a0027b922894421f703102c374c234c">handle</a> = cache.handle(<span class="stringliteral">&quot;my/identifier&quot;</span>_hs);</div>
</div><!-- fragment --><p>The idea behind a handle is the same of the flyweight pattern. In other terms, resources aren't copied around. Instead, instances are shared between handles. Users of a resource own a handle that guarantees that a resource isn't destroyed until all the handles are destroyed, even if the resource itself is removed from the cache.<br />
Handles are tiny objects both movable and copyable. They return the contained resource as a (possibly const) reference on request:</p>
<ul>
<li>By means of the <code>get</code> member function:</li>
</ul>
<div class="fragment"><div class="line"><span class="keyword">auto</span> &amp;resource = <a class="code" href="namespaceentt.html#a5a0027b922894421f703102c374c234c">handle</a>.get();</div>
</div><!-- fragment --><ul>
<li>Using the proper cast operator:</li>
</ul>
<div class="fragment"><div class="line"><span class="keyword">auto</span> &amp;resource = <a class="code" href="namespaceentt.html#a5a0027b922894421f703102c374c234c">handle</a>;</div>
</div><!-- fragment --><ul>
<li>Through the dereference operator:</li>
</ul>
<div class="fragment"><div class="line"><span class="keyword">auto</span> &amp;resource = *<a class="code" href="namespaceentt.html#a5a0027b922894421f703102c374c234c">handle</a>;</div>
</div><!-- fragment --><p>The resource can also be accessed directly using the arrow operator if required:</p>
<div class="fragment"><div class="line"><span class="keyword">auto</span> value = <a class="code" href="namespaceentt.html#a5a0027b922894421f703102c374c234c">handle</a>-&gt;value;</div>
</div><!-- fragment --><p>To test if a handle is still valid, the cast operator to <code>bool</code> allows users to use it in a guard:</p>
<div class="fragment"><div class="line"><span class="keywordflow">if</span>(handle) {</div>
<div class="line"> <span class="comment">// ...</span></div>
<div class="line">}</div>
</div><!-- fragment --><p>Finally, in case there is the need to load a resource and thus to get a handle without storing the resource itself in the cache, users can rely on the <code>temp</code> member function template.<br />
The declaration is similar to that of <code>load</code>, a (possibly invalid) handle for the resource is returned also in this case:</p>
<div class="fragment"><div class="line"><span class="keywordflow">if</span>(<span class="keyword">auto</span> handle = cache.temp&lt;my_loader&gt;(42); handle) {</div>
<div class="line"> <span class="comment">// ...</span></div>
<div class="line">}</div>
</div><!-- fragment --><p>Do not forget to test the handle for validity. Otherwise, getting a reference to the resource it points may result in undefined behavior. </p>
</div></div><!-- contents -->
</div><!-- PageDoc -->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
Generated by&#160;<a href="https://www.doxygen.org/index.html"><img class="footer" src="doxygen.svg" width="104" height="31" alt="doxygen"/></a> 1.9.1
</small></address>
</body>
</html>