Coverage Report

Created: 2026-10-28 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ogre/OgreMain/src/OgreResource.cpp
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
// Ogre includes
29
#include "OgreStableHeaders.h"
30
31
namespace Ogre 
32
{
33
    //-----------------------------------------------------------------------
34
    Resource::Resource(ResourceManager* creator, const String& name, ResourceHandle handle,
35
        const String& group, bool isManual, ManualResourceLoader* loader)
36
0
        : mCreator(creator), mName(name), mGroup(group), mHandle(handle), 
37
0
        mLoadingState(LOADSTATE_UNLOADED), mIsBackgroundLoaded(false),
38
0
        mIsManual(isManual), mSize(0),  mLoader(loader), mStateCount(0)
39
0
    {
40
0
    }
41
    //-----------------------------------------------------------------------
42
    Resource::~Resource() 
43
0
    { 
44
0
    }
45
    Resource& Resource::operator=(const Resource& rhs)
46
0
    {
47
0
        mName = rhs.mName;
48
0
        mGroup = rhs.mGroup;
49
0
        mCreator = rhs.mCreator;
50
0
        mIsManual = rhs.mIsManual;
51
0
        mLoader = rhs.mLoader;
52
0
        mHandle = rhs.mHandle;
53
0
        mSize = rhs.mSize;
54
55
0
        mLoadingState.store(rhs.mLoadingState.load());
56
0
        mIsBackgroundLoaded = rhs.mIsBackgroundLoaded;
57
58
0
        return *this;
59
0
    }
60
    //-----------------------------------------------------------------------
61
    void Resource::escalateLoading()
62
0
    {
63
        // Just call load as if this is the background thread, locking on
64
        // load status will prevent race conditions
65
0
        load(true);
66
0
        _fireLoadingComplete();
67
0
    }
68
    //-----------------------------------------------------------------------
69
    void Resource::prepare(bool background)
70
0
    {
71
        // quick check that avoids any synchronisation
72
0
        LoadingState old = mLoadingState.load();
73
0
        if (old != LOADSTATE_UNLOADED && old != LOADSTATE_PREPARING) return;
74
75
        // atomically do slower check to make absolutely sure,
76
        // and set the load state to PREPARING
77
0
        old = LOADSTATE_UNLOADED;
78
0
        if (!mLoadingState.compare_exchange_strong(old,LOADSTATE_PREPARING))
79
0
        {
80
0
            while( mLoadingState.load() == LOADSTATE_PREPARING )
81
0
            {
82
0
                            OGRE_LOCK_AUTO_MUTEX;
83
0
            }
84
85
0
            LoadingState state = mLoadingState.load();
86
0
            if( state != LOADSTATE_PREPARED && state != LOADSTATE_LOADING && state != LOADSTATE_LOADED )
87
0
            {
88
0
                OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Another thread failed in resource operation",
89
0
                    "Resource::prepare");
90
0
            }
91
0
            return;
92
0
        }
93
94
        // Scope lock for actual loading
95
0
        try
96
0
        {
97
98
0
                    OGRE_LOCK_AUTO_MUTEX;
99
100
0
            if (mIsManual)
101
0
            {
102
0
                if (mLoader)
103
0
                {
104
0
                    mLoader->prepareResource(this);
105
0
                }
106
0
                else
107
0
                {
108
                    // Warn that this resource is not reloadable
109
0
                    LogManager::getSingleton().stream(LML_TRIVIAL) 
110
0
                        << "Note: " << mCreator->getResourceType()
111
0
                        << " instance '" << mName << "' was defined as manually "
112
0
                        << "loaded, but no manual loader was provided. This Resource "
113
0
                        << "will be lost if it has to be reloaded.";
114
0
                }
115
0
            }
116
0
            else
117
0
            {
118
0
                if (mGroup == ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME)
119
0
                {
120
                    // Derive resource group
121
0
                    changeGroupOwnership(
122
0
                        ResourceGroupManager::getSingleton()
123
0
                        .findGroupContainingResource(mName));
124
0
                }
125
0
                prepareImpl();
126
0
            }
127
0
        }
128
0
        catch (...)
129
0
        {
130
0
            mLoadingState.store(LOADSTATE_UNLOADED);
131
132
0
            OGRE_LOCK_AUTO_MUTEX;
133
0
            unloadImpl();
134
135
0
            throw;
136
0
        }
137
138
0
        mLoadingState.store(LOADSTATE_PREPARED);
139
140
        // Since we don't distinguish between GPU and CPU RAM, this
141
        // seems pointless
142
        //if(mCreator)
143
        //  mCreator->_notifyResourcePrepared(this);
144
145
        // Fire events (if not background)
146
0
        if (!background)
147
0
            _firePreparingComplete();
148
149
150
0
    }
151
    //---------------------------------------------------------------------
152
    void Resource::load(bool background)
153
0
    {
154
        // Early-out without lock (mitigate perf cost of ensuring loaded)
155
        // Don't load if:
156
        // 1. We're already loaded
157
        // 2. Another thread is loading right now
158
        // 3. We're marked for background loading and this is not the background
159
        //    loading thread we're being called by
160
161
0
        if (mIsBackgroundLoaded && !background) return;
162
163
        // This next section is to deal with cases where 2 threads are fighting over
164
        // who gets to prepare / load - this will only usually happen if loading is escalated
165
0
        bool keepChecking = true;
166
0
        LoadingState old = LOADSTATE_UNLOADED;
167
0
        while (keepChecking)
168
0
        {
169
            // quick check that avoids any synchronisation
170
0
            old = mLoadingState.load();
171
172
0
            if ( old == LOADSTATE_PREPARING )
173
0
            {
174
0
                while( mLoadingState.load() == LOADSTATE_PREPARING )
175
0
                {
176
0
                                    OGRE_LOCK_AUTO_MUTEX;
177
0
                }
178
0
                old = mLoadingState.load();
179
0
            }
180
181
0
            if (old!=LOADSTATE_UNLOADED && old!=LOADSTATE_PREPARED && old!=LOADSTATE_LOADING) return;
182
183
            // atomically do slower check to make absolutely sure,
184
            // and set the load state to LOADING
185
0
            if (old==LOADSTATE_LOADING || !mLoadingState.compare_exchange_strong(old,LOADSTATE_LOADING))
186
0
            {
187
0
                while( mLoadingState.load() == LOADSTATE_LOADING )
188
0
                {
189
0
                                    OGRE_LOCK_AUTO_MUTEX;
190
0
                }
191
192
0
                LoadingState state = mLoadingState.load();
193
0
                if( state == LOADSTATE_PREPARED || state == LOADSTATE_PREPARING )
194
0
                {
195
                    // another thread is preparing, loop around
196
0
                    continue;
197
0
                }
198
0
                else if( state != LOADSTATE_LOADED )
199
0
                {
200
0
                    OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Another thread failed in resource operation",
201
0
                        "Resource::load");
202
0
                }
203
0
                return;
204
0
            }
205
0
            keepChecking = false;
206
0
        }
207
208
        // Scope lock for actual loading
209
0
        try
210
0
        {
211
212
0
                    OGRE_LOCK_AUTO_MUTEX;
213
214
215
216
0
            if (mIsManual)
217
0
            {
218
0
                if (old==LOADSTATE_UNLOADED && mLoader)
219
0
                {
220
0
                    mLoader->prepareResource(this);
221
222
0
                    if (!background)
223
0
                        _firePreparingComplete();
224
0
                }
225
226
0
                preLoadImpl();
227
228
                // Load from manual loader
229
0
                if (mLoader)
230
0
                {
231
0
                    mLoader->loadResource(this);
232
0
                }
233
0
                else
234
0
                {
235
                    // Warn that this resource is not reloadable
236
0
                    LogManager::getSingleton().stream(LML_TRIVIAL) 
237
0
                        << "Note: " << mCreator->getResourceType()
238
0
                        << " instance '" << mName << "' was defined as manually "
239
0
                        << "loaded, but no manual loader was provided. This Resource "
240
0
                        << "will be lost if it has to be reloaded.";
241
0
                }
242
0
                postLoadImpl();
243
0
            }
244
0
            else
245
0
            {
246
247
0
                if (old==LOADSTATE_UNLOADED)
248
0
                {
249
0
                    prepareImpl();
250
251
0
                    if (!background)
252
0
                        _firePreparingComplete();
253
0
                }
254
255
0
                preLoadImpl();
256
257
0
                if (mGroup == ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME)
258
0
                {
259
                    // Derive resource group
260
0
                    changeGroupOwnership(
261
0
                        ResourceGroupManager::getSingleton()
262
0
                        .findGroupContainingResource(mName));
263
0
                }
264
265
0
                loadImpl();
266
267
0
                postLoadImpl();
268
0
            }
269
270
            // Calculate resource size
271
0
            mSize = calculateSize();
272
273
0
        }
274
0
        catch (...)
275
0
        {
276
            // Reset loading in-progress flag, in case failed for some reason.
277
            // We reset it to UNLOADED because the only other case is when
278
            // old == PREPARED in which case the loadImpl should wipe out
279
            // any prepared data since it might be invalid.
280
0
            mLoadingState.store(LOADSTATE_UNLOADED);
281
282
0
            OGRE_LOCK_AUTO_MUTEX;
283
0
            unloadImpl();
284
285
            // Re-throw
286
0
            throw;
287
0
        }
288
289
0
        mLoadingState.store(LOADSTATE_LOADED);
290
0
        _dirtyState();
291
292
        // Notify manager
293
0
        if(mCreator)
294
0
            mCreator->_notifyResourceLoaded(this);
295
296
        // Fire events, if not background
297
0
        if (!background)
298
0
            _fireLoadingComplete();
299
300
301
0
    }
302
    //---------------------------------------------------------------------
303
    size_t Resource::calculateSize(void) const
304
0
    {
305
0
        size_t memSize = 0; // sizeof(*this) should be called by deriving classes
306
0
        memSize += mName.size() * sizeof(char);
307
0
        memSize += mGroup.size() * sizeof(char);
308
0
        memSize += mOrigin.size() * sizeof(char);
309
0
        memSize += sizeof(void*) * mListenerList.size();
310
311
0
        return memSize;
312
0
    }
313
    //---------------------------------------------------------------------
314
    void Resource::_dirtyState()
315
0
    {
316
        // don't worry about threading here, count only ever increases so 
317
        // doesn't matter if we get a lost increment (one is enough)
318
0
        ++mStateCount;  
319
0
    }
320
    //-----------------------------------------------------------------------
321
    void Resource::changeGroupOwnership(const String& newGroup)
322
0
    {
323
0
        if (mGroup != newGroup)
324
0
        {
325
0
            String oldGroup = mGroup;
326
0
            mGroup = newGroup;
327
0
            ResourceGroupManager::getSingleton()
328
0
                ._notifyResourceGroupChanged(oldGroup, this);
329
0
        }
330
0
    }
331
    //-----------------------------------------------------------------------
332
    void Resource::unload(void) 
333
0
    { 
334
        // Early-out without lock (mitigate perf cost of ensuring unloaded)
335
0
        LoadingState old = mLoadingState.load();
336
0
        if (old!=LOADSTATE_LOADED && old!=LOADSTATE_PREPARED) return;
337
338
339
0
        if (!mLoadingState.compare_exchange_strong(old,LOADSTATE_UNLOADING)) return;
340
341
        // Scope lock for actual unload
342
0
        {
343
0
                    OGRE_LOCK_AUTO_MUTEX;
344
0
            if (old==LOADSTATE_PREPARED) {
345
0
                unprepareImpl();
346
0
            } else {
347
0
                preUnloadImpl();
348
0
                unloadImpl();
349
0
                postUnloadImpl();
350
0
            }
351
0
        }
352
353
0
        mLoadingState.store(LOADSTATE_UNLOADED);
354
355
        // Notify manager
356
        // Note if we have gone from PREPARED to UNLOADED, then we haven't actually
357
        // unloaded, i.e. there is no memory freed on the GPU.
358
0
        if(old==LOADSTATE_LOADED && mCreator)
359
0
            mCreator->_notifyResourceUnloaded(this);
360
361
0
        _fireUnloadingComplete();
362
363
364
0
    }
365
    //-----------------------------------------------------------------------
366
    void Resource::reload(LoadingFlags flags)
367
0
    { 
368
0
            OGRE_LOCK_AUTO_MUTEX;
369
0
        if (mLoadingState.load() == LOADSTATE_LOADED)
370
0
        {
371
0
            unload();
372
0
            load();
373
0
        }
374
0
    }
375
    //-----------------------------------------------------------------------
376
    void Resource::touch(void) 
377
0
    {
378
        // make sure loaded
379
0
        load();
380
381
0
        if(mCreator)
382
0
            mCreator->_notifyResourceTouched(this);
383
0
    }
384
    //-----------------------------------------------------------------------
385
    void Resource::addListener(Resource::Listener* lis)
386
0
    {
387
0
            OGRE_LOCK_MUTEX(mListenerListMutex);
388
0
        mListenerList.insert(lis);
389
0
    }
390
    //-----------------------------------------------------------------------
391
    void Resource::removeListener(Resource::Listener* lis)
392
0
    {
393
        // O(n) but not called very often
394
0
            OGRE_LOCK_MUTEX(mListenerListMutex);
395
0
        mListenerList.erase(lis);
396
0
    }
397
    //-----------------------------------------------------------------------
398
    void Resource::_fireLoadingComplete(bool unused)
399
0
    {
400
0
        ListenerList listenerList;
401
0
        {
402
            // Lock the listener list and make a copy of it
403
0
            OGRE_LOCK_MUTEX(mListenerListMutex);
404
0
            listenerList = mListenerList;
405
0
        }
406
0
        for (auto& l : listenerList)
407
0
        {
408
0
            l->loadingComplete(this);
409
0
        }
410
0
    }
411
    //-----------------------------------------------------------------------
412
    void Resource::_firePreparingComplete(bool unused)
413
0
    {
414
0
        ListenerList listenerList;
415
0
        {
416
            // Lock the listener list and make a copy of it
417
0
            OGRE_LOCK_MUTEX(mListenerListMutex);
418
0
            listenerList = mListenerList;
419
0
        }
420
0
        for (auto& l : listenerList)
421
0
        {
422
0
            l->preparingComplete(this);
423
0
        }
424
0
    }
425
    //-----------------------------------------------------------------------
426
    void Resource::_fireUnloadingComplete(void)
427
0
    {
428
0
        ListenerList listenerList;
429
0
        {
430
            // Lock the listener list and make a copy of it
431
0
            OGRE_LOCK_MUTEX(mListenerListMutex);
432
0
            listenerList = mListenerList;
433
0
        }
434
0
        for (auto& l : listenerList)
435
0
        {
436
0
            l->unloadingComplete(this);
437
0
        }
438
0
    }
439
440
}