| 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__ |
|---|