Coverage Report

Created: 2026-02-26 07:02

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