/src/ogre/OgreMain/include/OgreInstanceManager.h
Line | Count | Source |
1 | | /* |
2 | | ----------------------------------------------------------------------------- |
3 | | This source file is part of OGRE |
4 | | (Object-oriented Graphics Rendering Engine) |
5 | | For the latest info, see http://www.ogre3d.org/ |
6 | | |
7 | | Copyright (c) 2000-2014 Torus Knot Software Ltd |
8 | | |
9 | | Permission is hereby granted, free of charge, to any person obtaining a copy |
10 | | of this software and associated documentation files (the "Software"), to deal |
11 | | in the Software without restriction, including without limitation the rights |
12 | | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
13 | | copies of the Software, and to permit persons to whom the Software is |
14 | | furnished to do so, subject to the following conditions: |
15 | | |
16 | | The above copyright notice and this permission notice shall be included in |
17 | | all copies or substantial portions of the Software. |
18 | | |
19 | | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
20 | | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
21 | | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
22 | | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
23 | | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
24 | | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
25 | | THE SOFTWARE. |
26 | | ----------------------------------------------------------------------------- |
27 | | */ |
28 | | #ifndef __InstanceManager_H__ |
29 | | #define __InstanceManager_H__ |
30 | | |
31 | | #include "OgrePrerequisites.h" |
32 | | #include "OgreRenderOperation.h" |
33 | | #include "OgreHeaderPrefix.h" |
34 | | |
35 | | namespace Ogre |
36 | | { |
37 | | /** \addtogroup Core |
38 | | * @{ |
39 | | */ |
40 | | /** \addtogroup Scene |
41 | | * @{ |
42 | | */ |
43 | | |
44 | | /** This is the main starting point for the new instancing system. |
45 | | Each InstanceManager can control one technique and one mesh, but it can manage |
46 | | multiple materials at the same time. |
47 | | @ref SceneManager::createInstanceManager, which creates this InstanceManager. Each one |
48 | | must have a unique name. It's wasteless to create two InstanceManagers with the same |
49 | | mesh reference, instancing technique and instances per batch count. |
50 | | This class takes care of managing batches automatically, so that more are created when |
51 | | needed, and reuse existing ones as much as possible; thus the user doesn't have to worry |
52 | | of managing all those low level issues. |
53 | | |
54 | | @see @ref InstanceBatch |
55 | | @see @ref InstancedEntity |
56 | | @see Design discussion thread: http://www.ogre3d.org/forums/viewtopic.php?f=4&t=59902 |
57 | | */ |
58 | | class _OgreExport InstanceManager : public FactoryAlloc |
59 | | { |
60 | | public: |
61 | | enum InstancingTechnique |
62 | | { |
63 | | ShaderBased, ///< %Any SM 2.0+ @ref InstanceBatchShader |
64 | | TextureVTF, ///< Needs Vertex Texture Fetch & SM 3.0+ @ref InstanceBatchVTF |
65 | | HWInstancingBasic, ///< Needs SM 3.0+ and HW instancing support @ref InstanceBatchHW |
66 | | HWInstancingVTF, ///< Needs SM 3.0+, HW instancing support & VTF @ref InstanceBatchHW_VTF |
67 | | InstancingTechniquesCount |
68 | | }; |
69 | | |
70 | | /** Values to be used in setSetting() & BatchSettings::setting */ |
71 | | enum BatchSettingId |
72 | | { |
73 | | /// Makes all batches from same material cast shadows |
74 | | CAST_SHADOWS = 0, |
75 | | /// Makes each batch to display it's bounding box. Useful for debugging or profiling |
76 | | SHOW_BOUNDINGBOX, |
77 | | |
78 | | NUM_SETTINGS |
79 | | }; |
80 | | |
81 | | private: |
82 | | struct BatchSettings |
83 | | { |
84 | | //These are all per material |
85 | | bool setting[NUM_SETTINGS]; |
86 | | |
87 | | BatchSettings() |
88 | 0 | { |
89 | 0 | setting[CAST_SHADOWS] = true; |
90 | 0 | setting[SHOW_BOUNDINGBOX] = false; |
91 | 0 | } |
92 | | }; |
93 | | |
94 | | typedef std::vector<InstanceBatch*> InstanceBatchVec; //vec[batchN] = Batch |
95 | | typedef std::map<String, InstanceBatchVec> InstanceBatchMap; //map[materialName] = Vec |
96 | | |
97 | | typedef std::map<String, BatchSettings> BatchSettingsMap; |
98 | | |
99 | | const String mName; //Not the name of the mesh |
100 | | MeshPtr mMeshReference; |
101 | | InstanceBatchMap mInstanceBatches; |
102 | | size_t mIdCount; |
103 | | |
104 | | InstanceBatchVec mDirtyBatches; |
105 | | |
106 | | RenderOperation mSharedRenderOperation; |
107 | | |
108 | | size_t mInstancesPerBatch; |
109 | | InstancingTechnique mInstancingTechnique; |
110 | | uint16 mInstancingFlags; ///< @see InstanceManagerFlags |
111 | | unsigned short mSubMeshIdx; |
112 | | |
113 | | BatchSettingsMap mBatchSettings; |
114 | | SceneManager* mSceneManager; |
115 | | |
116 | | size_t mMaxLookupTableInstances; |
117 | | unsigned char mNumCustomParams; //Number of custom params per instance. |
118 | | |
119 | | /** Finds a batch with at least one free instanced entity we can use. |
120 | | If none found, creates one. |
121 | | */ |
122 | | inline InstanceBatch* getFreeBatch( const String &materialName ); |
123 | | |
124 | | /** Called when batches are fully exhausted (can't return more instances) so a new batch |
125 | | is created. |
126 | | For the first time use, it can take big build time. |
127 | | It takes care of getting the render operation which will be shared by further batches, |
128 | | which decreases their build time, and prevents GPU RAM from skyrocketing. |
129 | | @param materialName The material name, to know where to put this batch in the map |
130 | | @param firstTime True if this is the first time it is called |
131 | | @return The created InstancedManager for convenience |
132 | | */ |
133 | | InstanceBatch* buildNewBatch( const String &materialName, bool firstTime ); |
134 | | |
135 | | /** @see defragmentBatches overload, this takes care of an array of batches |
136 | | for a specific material */ |
137 | | void defragmentBatches( bool optimizeCull, std::vector<InstancedEntity*> &entities, |
138 | | std::vector<Ogre::Vector4f> &usedParams, |
139 | | InstanceBatchVec &fragmentedBatches ); |
140 | | |
141 | | /** @see setSetting. This function helps it by setting the given parameter to all batches |
142 | | in container. |
143 | | */ |
144 | | void applySettingToBatches( BatchSettingId id, bool value, const InstanceBatchVec &container ); |
145 | | |
146 | | /** Called when we you use a mesh which has shared vertices, the function creates separate |
147 | | vertex/index buffers and also recreates the bone assignments. |
148 | | */ |
149 | | static void unshareVertices(const Ogre::MeshPtr &mesh); |
150 | | |
151 | | public: |
152 | | InstanceManager( const String &customName, SceneManager *sceneManager, |
153 | | const String &meshName, const String &groupName, |
154 | | InstancingTechnique instancingTechnique, uint16 instancingFlags, |
155 | | size_t instancesPerBatch, unsigned short subMeshIdx, bool useBoneMatrixLookup = false); |
156 | | ~InstanceManager(); |
157 | | |
158 | 0 | const String& getName() const { return mName; } |
159 | | |
160 | 0 | SceneManager* getSceneManager() const { return mSceneManager; } |
161 | | |
162 | | /** Raises an exception if trying to change it after creating the first InstancedEntity |
163 | | The actual value may be less if the technique doesn't support having so much. |
164 | | See @ref getMaxOrBestNumInstancesPerBatch for the usefulness of this function |
165 | | @param instancesPerBatch New instances per batch number |
166 | | */ |
167 | | void setInstancesPerBatch( size_t instancesPerBatch ); |
168 | | |
169 | | /** Sets the size of the lookup table for techniques supporting bone lookup table. |
170 | | Raises an exception if trying to change it after creating the first InstancedEntity. |
171 | | Setting this value below the number of unique (non-sharing) entity instance animations |
172 | | will produce a crash during runtime. Setting this value above will increase memory |
173 | | consumption and reduce framerate. |
174 | | @remarks The value should be as close but not below the actual value. |
175 | | @param maxLookupTableInstances New size of the lookup table |
176 | | */ |
177 | | void setMaxLookupTableInstances( size_t maxLookupTableInstances ); |
178 | | |
179 | | /** Sets the number of custom parameters per instance. Some techniques (i.e. HWInstancingBasic) |
180 | | support this, but not all of them. They also may have limitations to the max number. All |
181 | | instancing implementations assume each instance param is a Vector4 (4 floats). |
182 | | |
183 | | This function cannot be called after the first batch has been created. Otherwise |
184 | | it will raise an exception. If the technique doesn't support custom params, it will |
185 | | raise an exception at the time of building the first InstanceBatch. |
186 | | |
187 | | HWInstancingBasic: |
188 | | * Each custom params adds an additional float4 TEXCOORD. |
189 | | HWInstancingVTF: |
190 | | * Not implemented. (Recommendation: Implement this as an additional float4 VTF fetch) |
191 | | TextureVTF: |
192 | | * Not implemented. (see HWInstancingVTF's recommendation) |
193 | | ShaderBased: |
194 | | * Not supported. |
195 | | @param numCustomParams Number of custom parameters each instance will have. Default: 0 |
196 | | */ |
197 | | void setNumCustomParams( unsigned char numCustomParams ); |
198 | | |
199 | | unsigned char getNumCustomParams() const |
200 | 0 | { return mNumCustomParams; } |
201 | | |
202 | | /** @return Instancing technique this manager was created for. Can't be changed after creation */ |
203 | | InstancingTechnique getInstancingTechnique() const |
204 | 0 | { return mInstancingTechnique; } |
205 | | |
206 | | /** Calculates the maximum (or the best amount, depending on flags) of instances |
207 | | per batch given the suggested size for the technique this manager was created for. |
208 | | |
209 | | This is done automatically when creating an instanced entity, but this function in conjunction |
210 | | with @ref setInstancesPerBatch allows more flexible control over the amount of instances |
211 | | per batch |
212 | | @param materialName Name of the material to base on |
213 | | @param suggestedSize Suggested amount of instances per batch |
214 | | @param flags @ref InstanceManagerFlags to pass to the InstanceManager |
215 | | @return The max/best amount of instances per batch given the suggested size and flags |
216 | | */ |
217 | | size_t getMaxOrBestNumInstancesPerBatch( const String &materialName, size_t suggestedSize, uint16 flags ); |
218 | | |
219 | | /// Creates an InstancedEntity |
220 | | InstancedEntity* createInstancedEntity( const String &materialName ); |
221 | | |
222 | | /** This function can be useful to improve CPU speed after having too many instances |
223 | | created, which where now removed, thus freeing many batches with zero used Instanced Entities |
224 | | However the batches aren't automatically removed from memory until the InstanceManager is |
225 | | destroyed, or this function is called. This function removes those batches which are completely |
226 | | unused (only wasting memory). |
227 | | */ |
228 | | void cleanupEmptyBatches(void); |
229 | | |
230 | | /** After creating many entities (which turns in many batches) and then removing entities that |
231 | | are in the middle of these batches, there might be many batches with many free entities. |
232 | | Worst case scenario, there could be left one batch per entity. Imagine there can be |
233 | | 80 entities per batch, there are 80 batches, making a total of 6400 entities. Then |
234 | | 6320 of those entities are removed in a very specific way, which leads to having |
235 | | 80 batches, 80 entities, and GPU vertex shader still needs to process 6400! |
236 | | This is called fragmentation. This function reparents the InstancedEntities |
237 | | to fewer batches, in this case leaving only one batch with 80 entities |
238 | | |
239 | | |
240 | | @note This function takes time. Make sure to call this only when you're sure there's |
241 | | too much of fragmentation and you won't be creating more InstancedEntities soon |
242 | | Also in many cases cleanupEmptyBatches() ought to be enough |
243 | | Defragmentation is done per material |
244 | | Static batches won't be defragmented. If you want to degragment them, set them |
245 | | to dynamic again, and switch back to static after calling this function. |
246 | | |
247 | | @param optimizeCulling When true, entities close together will be reorganized |
248 | | in the same batch for more efficient CPU culling. This can take more CPU |
249 | | time. You want this to be false if you now you're entities are moving very |
250 | | randomly which tends them to get separated and spread all over the scene |
251 | | (which nullifies any CPU culling) |
252 | | */ |
253 | | void defragmentBatches( bool optimizeCulling ); |
254 | | |
255 | | /** Applies a setting for all batches using the same material |
256 | | |
257 | | If the material name hasn't been used, the settings are still stored |
258 | | This allows setting up batches before they get even created. |
259 | | @par Examples |
260 | | `setSetting(InstanceManager::CAST_SHADOWS, false, "")` disables shadow |
261 | | casting for all instanced entities (see @ref MovableObject::setCastShadows) |
262 | | @par |
263 | | `setSetting(InstanceManager::SHOW_BOUNDINGBOX, true, "MyMat")` |
264 | | will display the bounding box of the batch (not individual InstancedEntities) |
265 | | from all batches using material "MyMat" |
266 | | @param id @ref BatchSettingId to setup |
267 | | @param enabled Boolean value. It's meaning depends on the id. |
268 | | @param materialName When Blank, the setting is applied to all existing materials |
269 | | */ |
270 | | void setSetting( BatchSettingId id, bool enabled, const String &materialName = BLANKSTRING ); |
271 | | |
272 | | /// If settings for the given material didn't exist, default value is returned |
273 | | bool getSetting( BatchSettingId id, const String &materialName ) const; |
274 | | |
275 | | /** Returns true if settings were already created for the given material name. |
276 | | If false is returned, it means getSetting will return default settings. |
277 | | */ |
278 | | bool hasSettings( const String &materialName ) const; |
279 | | |
280 | | /** @copydoc InstanceBatch::setStaticAndUpdate */ |
281 | | void setBatchesAsStaticAndUpdate( bool bStatic ); |
282 | | |
283 | | /** Called by an InstanceBatch when it requests their bounds to be updated for proper culling |
284 | | @param dirtyBatch The batch which is dirty, usually same as caller. |
285 | | */ |
286 | | void _addDirtyBatch( InstanceBatch *dirtyBatch ); |
287 | | |
288 | | /** Called by SceneManager when we told it we have at least one dirty batch */ |
289 | | void _updateDirtyBatches(void); |
290 | | |
291 | | typedef ConstMapIterator<InstanceBatchMap> InstanceBatchMapIterator; |
292 | | typedef ConstVectorIterator<InstanceBatchVec> InstanceBatchIterator; |
293 | | |
294 | | /// Get non-updateable iterator over instance batches per material |
295 | | InstanceBatchMapIterator getInstanceBatchMapIterator(void) const |
296 | 0 | { return InstanceBatchMapIterator( mInstanceBatches.begin(), mInstanceBatches.end() ); } |
297 | | |
298 | | /** Get non-updateable iterator over instance batches for given material |
299 | | |
300 | | Each InstanceBatch pointer may be modified for low level usage (i.e. |
301 | | setCustomParameter), but there's no synchronization mechanism when |
302 | | multithreading or creating more instances, that's up to the user. |
303 | | */ |
304 | | InstanceBatchIterator getInstanceBatchIterator( const String &materialName ) const; |
305 | | }; |
306 | | } // namespace Ogre |
307 | | |
308 | | #include "OgreHeaderSuffix.h" |
309 | | |
310 | | #endif // __InstanceManager_H__ |