Coverage Report

Created: 2025-12-25 06:34

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