/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 | | } |