Coverage Report

Created: 2025-12-14 06:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ogre/OgreMain/src/OgreRenderTarget.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
#include "OgreStableHeaders.h"
29
30
#include "OgreViewport.h"
31
#include "OgreRenderTargetListener.h"
32
#include "OgreTimer.h"
33
34
namespace Ogre {
35
36
    RenderTarget::RenderTarget()
37
0
        : mPriority(OGRE_DEFAULT_RT_GROUP)
38
0
        , mDepthBufferPoolId(RBP_DEFAULT)
39
0
        , mDepthBuffer(0)
40
0
        , mActive(true)
41
0
        , mAutoUpdate(true)
42
0
        , mHwGamma(false)
43
0
        , mFSAA(0)
44
#if OGRE_NO_QUAD_BUFFER_STEREO == 0
45
    , mStereoEnabled(true)
46
#else
47
0
    , mStereoEnabled(false)
48
#endif
49
0
    {
50
0
        mTimer = Root::getSingleton().getTimer();
51
0
        resetStatistics();
52
0
    }
53
54
    RenderTarget::~RenderTarget()
55
0
    {
56
        // make a copy of the list to avoid crashes, the viewport destructor change the list
57
0
        ViewportList vlist = mViewportList;
58
        
59
        // Delete viewports
60
0
        for (auto & i : vlist)
61
0
        {
62
0
            fireViewportRemoved(i.second);
63
0
            OGRE_DELETE i.second;
64
0
        }
65
66
        //DepthBuffer keeps track of us, avoid a dangling pointer
67
0
        detachDepthBuffer();
68
69
70
        // Write closing message
71
0
        LogManager::getSingleton().stream(LML_TRIVIAL)
72
0
            << "Render Target '" << mName << "' "
73
0
            << "Average FPS: " << mStats.avgFPS << " "
74
0
            << "Best FPS: " << mStats.bestFPS << " "
75
0
            << "Worst FPS: " << mStats.worstFPS; 
76
77
0
    }
78
79
    void RenderTarget::getMetrics(unsigned int& width, unsigned int& height)
80
0
    {
81
0
        width = mWidth;
82
0
        height = mHeight;
83
0
    }
84
    //-----------------------------------------------------------------------
85
    void RenderTarget::setDepthBufferPool( uint16 poolId )
86
0
    {
87
0
        if( mDepthBufferPoolId != poolId )
88
0
        {
89
0
            mDepthBufferPoolId = poolId;
90
0
            detachDepthBuffer();
91
0
        }
92
0
    }
93
    //-----------------------------------------------------------------------
94
    bool RenderTarget::attachDepthBuffer( DepthBuffer *depthBuffer )
95
0
    {
96
0
        bool retVal = false;
97
98
0
        if( (retVal = depthBuffer->isCompatible( this )) )
99
0
        {
100
0
            detachDepthBuffer();
101
0
            mDepthBuffer = depthBuffer;
102
0
            mDepthBuffer->_notifyRenderTargetAttached( this );
103
0
        }
104
105
0
        return retVal;
106
0
    }
107
    //-----------------------------------------------------------------------
108
    void RenderTarget::detachDepthBuffer()
109
0
    {
110
0
        if( mDepthBuffer )
111
0
        {
112
0
            mDepthBuffer->_notifyRenderTargetDetached( this );
113
0
            _detachDepthBuffer();
114
0
        }
115
0
    }
116
117
    void RenderTarget::updateImpl(void)
118
0
    {
119
0
        _beginUpdate();
120
0
        _updateAutoUpdatedViewports(true);
121
0
        _endUpdate();
122
0
    }
123
124
    void RenderTarget::_beginUpdate()
125
0
    {
126
        // notify listeners (pre)
127
0
        firePreUpdate();
128
129
0
        mStats.triangleCount = 0;
130
0
        mStats.batchCount = 0;
131
0
    }
132
133
    void RenderTarget::_updateAutoUpdatedViewports(bool updateStatistics)
134
0
    {
135
        // Go through viewports in Z-order
136
        // Tell each to refresh
137
0
        for (const auto& v : mViewportList)
138
0
        {
139
0
            Viewport* viewport = v.second;
140
0
            if(viewport->isAutoUpdated())
141
0
            {
142
0
                _updateViewport(viewport,updateStatistics);
143
0
            }
144
0
        }
145
0
    }
146
147
    void RenderTarget::_endUpdate()
148
0
    {
149
         // notify listeners (post)
150
0
        firePostUpdate();
151
152
        // Update statistics (always on top)
153
0
        updateStats();
154
0
    }
155
156
    void RenderTarget::_updateViewport(Viewport* viewport, bool updateStatistics)
157
0
    {
158
0
        assert(viewport->getTarget() == this &&
159
0
                "RenderTarget::_updateViewport the requested viewport is "
160
0
                "not bound to the rendertarget!");
161
162
0
        fireViewportPreUpdate(viewport);
163
0
        viewport->update();
164
0
        if(updateStatistics)
165
0
        {
166
0
            mStats.triangleCount += viewport->_getNumRenderedFaces();
167
0
            mStats.batchCount += viewport->_getNumRenderedBatches();
168
0
        }
169
0
        fireViewportPostUpdate(viewport);
170
0
    }
171
172
    Viewport* RenderTarget::addViewport(Camera* cam, int ZOrder, FloatRect rect)
173
0
    {       
174
        // Check no existing viewport with this Z-order
175
0
        ViewportList::iterator it = mViewportList.find(ZOrder);
176
177
0
        if (it != mViewportList.end())
178
0
        {
179
0
            StringStream str;
180
0
            str << "Can't create another viewport for "
181
0
                << mName << " with Z-order " << ZOrder
182
0
                << " because a viewport exists with this Z-order already.";
183
0
            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, str.str(), "RenderTarget::addViewport");
184
0
        }
185
        // Add viewport to list
186
        // Order based on Z-order
187
0
        Viewport* vp = OGRE_NEW Viewport(cam, this, rect, ZOrder);
188
189
0
        mViewportList.emplace(ZOrder, vp);
190
191
0
        fireViewportAdded(vp);
192
193
0
        return vp;
194
0
    }
195
    //-----------------------------------------------------------------------
196
    void RenderTarget::removeViewport(int ZOrder)
197
0
    {
198
0
        ViewportList::iterator it = mViewportList.find(ZOrder);
199
200
0
        if (it != mViewportList.end())
201
0
        {
202
0
            fireViewportRemoved(it->second);
203
0
            OGRE_DELETE (*it).second;
204
0
            mViewportList.erase(ZOrder);
205
0
        }
206
0
    }
207
208
    void RenderTarget::removeAllViewports(void)
209
0
    {
210
        // make a copy of the list to avoid crashes, the viewport destructor change the list
211
0
        ViewportList vlist = mViewportList;
212
213
0
        for (auto& vl : vlist)
214
0
        {
215
0
            fireViewportRemoved(vl.second);
216
0
            OGRE_DELETE vl.second;
217
0
        }
218
219
0
        mViewportList.clear();
220
221
0
    }
222
223
    void RenderTarget::resetStatistics(void)
224
0
    {
225
0
        mStats.avgFPS = 0.0;
226
0
        mStats.bestFPS = 0.0;
227
0
        mStats.lastFPS = 0.0;
228
0
        mStats.worstFPS = 999.0;
229
0
        mStats.triangleCount = 0;
230
0
        mStats.batchCount = 0;
231
0
        mStats.bestFrameTime = 999999;
232
0
        mStats.worstFrameTime = 0;
233
0
        mStats.vBlankMissCount = -1;
234
235
0
        mLastTime = mTimer->getMilliseconds();
236
0
        mLastSecond = mLastTime;
237
0
        mFrameCount = 0;
238
0
    }
239
240
    void RenderTarget::updateStats(void)
241
0
    {
242
0
        ++mFrameCount;
243
0
        unsigned long thisTime = mTimer->getMilliseconds();
244
245
        // check frame time
246
0
        unsigned long frameTime = thisTime - mLastTime ;
247
0
        mLastTime = thisTime ;
248
249
0
        mStats.bestFrameTime = std::min(mStats.bestFrameTime, frameTime);
250
0
        mStats.worstFrameTime = std::max(mStats.worstFrameTime, frameTime);
251
252
        // check if new second (update only once per second)
253
0
        if (thisTime - mLastSecond > 1000) 
254
0
        { 
255
            // new second - not 100% precise
256
0
            mStats.lastFPS = (float)mFrameCount / (float)(thisTime - mLastSecond) * 1000.0f;
257
258
0
            if (mStats.avgFPS == 0)
259
0
                mStats.avgFPS = mStats.lastFPS;
260
0
            else
261
0
                mStats.avgFPS = (mStats.avgFPS + mStats.lastFPS) / 2; // not strictly correct, but good enough
262
263
0
            mStats.bestFPS = std::max(mStats.bestFPS, mStats.lastFPS);
264
0
            mStats.worstFPS = std::min(mStats.worstFPS, mStats.lastFPS);
265
266
0
            mLastSecond = thisTime ;
267
0
            mFrameCount  = 0;
268
269
0
        }
270
271
0
    }
272
273
    void RenderTarget::getCustomAttribute(const String& name, void* pData)
274
0
    {
275
0
        OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Attribute not found. " + name, " RenderTarget::getCustomAttribute");
276
0
    }
277
    //-----------------------------------------------------------------------
278
    void RenderTarget::addListener(RenderTargetListener* listener)
279
0
    {
280
0
        if (std::find(mListeners.begin(), mListeners.end(), listener) == mListeners.end())
281
0
            mListeners.push_back(listener);
282
0
    }
283
    //-----------------------------------------------------------------------
284
    void RenderTarget::insertListener(RenderTargetListener* listener, const unsigned int pos)
285
0
    {
286
        // if the position is larger than the list size we just set the listener at the end of the list
287
0
        if (pos > mListeners.size())
288
0
            mListeners.push_back(listener);
289
0
        else
290
0
            mListeners.insert(mListeners.begin() + pos, listener);
291
0
    }
292
    //-----------------------------------------------------------------------
293
    void RenderTarget::removeListener(RenderTargetListener* listener)
294
0
    {
295
0
        RenderTargetListenerList::iterator i = std::find(mListeners.begin(), mListeners.end(), listener);
296
0
        if (i != mListeners.end())
297
0
            mListeners.erase(i);
298
0
    }
299
    //-----------------------------------------------------------------------
300
    void RenderTarget::removeAllListeners(void)
301
0
    {
302
0
        mListeners.clear();
303
0
    }
304
    //-----------------------------------------------------------------------
305
    void RenderTarget::firePreUpdate(void)
306
0
    {
307
0
        RenderTargetEvent evt;
308
0
        evt.source = this;
309
310
0
        for(auto& l : mListeners)
311
0
        {
312
0
            l->preRenderTargetUpdate(evt);
313
0
        }
314
315
316
0
    }
317
    //-----------------------------------------------------------------------
318
    void RenderTarget::firePostUpdate(void)
319
0
    {
320
0
        RenderTargetEvent evt;
321
0
        evt.source = this;
322
323
0
        for(auto& l : mListeners)
324
0
        {
325
0
            l->postRenderTargetUpdate(evt);
326
0
        }
327
0
    }
328
    //-----------------------------------------------------------------------
329
    Viewport* RenderTarget::getViewport(unsigned short index)
330
0
    {
331
0
        assert (index < mViewportList.size() && "Index out of bounds");
332
333
0
        ViewportList::iterator i = mViewportList.begin();
334
0
        while (index--)
335
0
            ++i;
336
0
        return i->second;
337
0
    }
338
    //-----------------------------------------------------------------------
339
    Viewport* RenderTarget::getViewportByZOrder(int ZOrder)
340
0
    {
341
0
        ViewportList::iterator i = mViewportList.find(ZOrder);
342
0
        if(i == mViewportList.end())
343
0
        {
344
0
            OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND,"No viewport with given Z-order: "
345
0
                + StringConverter::toString(ZOrder), "RenderTarget::getViewportByZOrder");
346
0
        }
347
0
        return i->second;
348
0
    }
349
    //-----------------------------------------------------------------------
350
    bool RenderTarget::hasViewportWithZOrder(int ZOrder)
351
0
    {
352
0
        ViewportList::iterator i = mViewportList.find(ZOrder);
353
0
        return i != mViewportList.end();
354
0
    }
355
    //-----------------------------------------------------------------------
356
    void RenderTarget::fireViewportPreUpdate(Viewport* vp)
357
0
    {
358
0
        RenderTargetViewportEvent evt;
359
0
        evt.source = vp;
360
361
0
        for(auto& l : mListeners)
362
0
        {
363
0
            l->preViewportUpdate(evt);
364
0
        }
365
0
    }
366
    //-----------------------------------------------------------------------
367
    void RenderTarget::fireViewportPostUpdate(Viewport* vp)
368
0
    {
369
0
        RenderTargetViewportEvent evt;
370
0
        evt.source = vp;
371
372
0
        for(auto& l : mListeners)
373
0
        {
374
0
            l->postViewportUpdate(evt);
375
0
        }
376
0
    }
377
    //-----------------------------------------------------------------------
378
    void RenderTarget::fireViewportAdded(Viewport* vp)
379
0
    {
380
0
        RenderTargetViewportEvent evt;
381
0
        evt.source = vp;
382
383
0
        for(auto& l : mListeners)
384
0
        {
385
0
            l->viewportAdded(evt);
386
0
        }
387
0
    }
388
    //-----------------------------------------------------------------------
389
    void RenderTarget::fireViewportRemoved(Viewport* vp)
390
0
    {
391
0
        RenderTargetViewportEvent evt;
392
0
        evt.source = vp;
393
394
        // Make a temp copy of the listeners
395
        // some will want to remove themselves as listeners when they get this
396
0
        RenderTargetListenerList tempList = mListeners;
397
398
0
        for(auto& l : tempList)
399
0
        {
400
0
            l->viewportRemoved(evt);
401
0
        }
402
0
    }
403
    //-----------------------------------------------------------------------
404
    String RenderTarget::writeContentsToTimestampedFile(const String& filenamePrefix, const String& filenameSuffix)
405
0
    {
406
0
        auto t = std::time(nullptr);
407
0
        auto pTime = std::localtime(&t);
408
409
        // use ISO 8601 order
410
0
        StringStream oss;
411
0
        oss << filenamePrefix
412
0
            << std::put_time(pTime, "%Y%m%d_%H%M%S")
413
0
            << std::setw(3) << std::setfill('0') << (mTimer->getMilliseconds() % 1000)
414
0
            << filenameSuffix;
415
0
        String filename = oss.str();
416
0
        writeContentsToFile(filename);
417
0
        return filename;
418
419
0
    }
420
    //-----------------------------------------------------------------------
421
    void RenderTarget::writeContentsToFile(const String& filename)
422
0
    {
423
0
        Image img(suggestPixelFormat(), mWidth, mHeight);
424
425
0
        PixelBox pb = img.getPixelBox();
426
0
        copyContentsToMemory(pb, pb);
427
428
0
        img.save(filename);
429
0
    }
430
    //-----------------------------------------------------------------------
431
    void RenderTarget::_notifyCameraRemoved(const Camera* cam)
432
0
    {
433
0
        ViewportList::iterator i, iend;
434
0
        iend = mViewportList.end();
435
0
        for (auto& l : mViewportList)
436
0
        {
437
0
            Viewport* v = l.second;
438
0
            if (v->getCamera() == cam)
439
0
            {
440
                // disable camera link
441
0
                v->setCamera(0);
442
0
            }
443
0
        }
444
0
    }
445
    //-----------------------------------------------------------------------
446
    void RenderTarget::update(bool swap)
447
0
    {
448
0
        GpuEventScope profileScope(mName);
449
        // call implementation
450
0
        updateImpl();
451
452
453
0
        if (swap)
454
0
        {
455
            // Swap buffers
456
0
            swapBuffers();
457
0
        }
458
0
    }
459
}