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