[148] | 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__ |
---|