/** * @file llscenemonitor.cpp * @brief monitor the scene loading process. * * $LicenseInfo:firstyear=2003&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "llviewerprecompiledheaders.h" #include "llrendertarget.h" #include "llscenemonitor.h" #include "llviewerwindow.h" #include "llviewerdisplay.h" #include "llviewercontrol.h" #include "llviewershadermgr.h" #include "llui.h" #include "llstartup.h" #include "llappviewer.h" #include "llwindow.h" #include "llpointer.h" #include "llspatialpartition.h" #include "llagent.h" LLSceneMonitorView* gSceneMonitorView = NULL; // //The procedures of monitoring when the scene finishes loading visually, //i.e., no pixel differences among frames, are: //1, freeze all dynamic objects and avatars; //2, (?) disable all sky and water; //3, capture frames periodically, by calling "capture()"; //4, compute pixel differences between two latest captured frames, by calling "compare()", results are stored at mDiff; //5, compute the number of pixels in mDiff above some tolerance threshold in GPU, by calling "queryDiff() -> calcDiffAggregate()"; //6, use gl occlusion query to fetch the result from GPU, by calling "fetchQueryResult()"; //END. // LLSceneMonitor::LLSceneMonitor() : mEnabled(FALSE), mDiff(NULL), mDiffResult(0.f), mDiffTolerance(0.1f), mCurTarget(NULL), mNeedsUpdateDiff(FALSE), mHasNewDiff(FALSE), mHasNewQueryResult(FALSE), mDebugViewerVisible(FALSE), mQueryObject(0), mSamplingTime(1.0f), mDiffPixelRatio(0.5f) { mFrames[0] = NULL; mFrames[1] = NULL; mRecording = new LLTrace::ExtendableRecording(); mRecording->start(); } LLSceneMonitor::~LLSceneMonitor() { destroyClass(); } void LLSceneMonitor::destroyClass() { reset(); delete mRecording; mRecording = NULL; } void LLSceneMonitor::reset() { delete mFrames[0]; delete mFrames[1]; delete mDiff; mFrames[0] = NULL; mFrames[1] = NULL; mDiff = NULL; mCurTarget = NULL; unfreezeScene(); if(mQueryObject > 0) { release_occlusion_query_object_name(mQueryObject); mQueryObject = 0; } } void LLSceneMonitor::setDebugViewerVisible(BOOL visible) { mDebugViewerVisible = visible; } bool LLSceneMonitor::preCapture() { static LLCachedControl monitor_enabled(gSavedSettings,"SceneLoadingMonitorEnabled"); static LLFrameTimer timer; mCurTarget = NULL; if (!LLGLSLShader::sNoFixedFunction) { return false; } BOOL enabled = (BOOL)monitor_enabled || mDebugViewerVisible; if(mEnabled != enabled) { if(mEnabled) { reset(); unfreezeScene(); } else { freezeScene(); } mEnabled = enabled; } if(!mEnabled) { return false; } if(gAgent.isPositionChanged()) { mRecording->reset(); } if(timer.getElapsedTimeF32() < mSamplingTime) { return false; } timer.reset(); S32 width = gViewerWindow->getWorldViewWidthRaw(); S32 height = gViewerWindow->getWorldViewHeightRaw(); if(!mFrames[0]) { mFrames[0] = new LLRenderTarget(); mFrames[0]->allocate(width, height, GL_RGB, false, false, LLTexUnit::TT_TEXTURE, true); gGL.getTexUnit(0)->bind(mFrames[0]); gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); mCurTarget = mFrames[0]; } else if(!mFrames[1]) { mFrames[1] = new LLRenderTarget(); mFrames[1]->allocate(width, height, GL_RGB, false, false, LLTexUnit::TT_TEXTURE, true); gGL.getTexUnit(0)->bind(mFrames[1]); gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); mCurTarget = mFrames[1]; } else //swap { mCurTarget = mFrames[0]; mFrames[0] = mFrames[1]; mFrames[1] = mCurTarget; } if(mCurTarget->getWidth() != width || mCurTarget->getHeight() != height) //size changed { mCurTarget->resize(width, height, GL_RGB); } return true; } void LLSceneMonitor::freezeAvatar(LLCharacter* avatarp) { mAvatarPauseHandles.push_back(avatarp->requestPause()); } void LLSceneMonitor::freezeScene() { //freeze all avatars for (std::vector::iterator iter = LLCharacter::sInstances.begin(); iter != LLCharacter::sInstances.end(); ++iter) { freezeAvatar((LLCharacter*)(*iter)); } // freeze everything else gSavedSettings.setBOOL("FreezeTime", TRUE); } void LLSceneMonitor::unfreezeScene() { //thaw all avatars mAvatarPauseHandles.clear(); // thaw everything else gSavedSettings.setBOOL("FreezeTime", FALSE); } void LLSceneMonitor::capture() { static U32 last_capture_time = 0; if(last_capture_time == gFrameCount) { return; } last_capture_time = gFrameCount; preCapture(); if(!mCurTarget) { return; } U32 old_FBO = LLRenderTarget::sCurFBO; gGL.getTexUnit(0)->bind(mCurTarget); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); //point to the main frame buffer. glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, mCurTarget->getWidth(), mCurTarget->getHeight()); //copy the content glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, old_FBO); mCurTarget = NULL; mNeedsUpdateDiff = TRUE; } bool LLSceneMonitor::needsUpdate() const { return mNeedsUpdateDiff; } void LLSceneMonitor::compare() { if(!mNeedsUpdateDiff) { return; } mNeedsUpdateDiff = FALSE; if(!mFrames[0] || !mFrames[1]) { return; } if(mFrames[0]->getWidth() != mFrames[1]->getWidth() || mFrames[0]->getHeight() != mFrames[1]->getHeight()) { return; //size does not match } S32 width = gViewerWindow->getWindowWidthRaw(); S32 height = gViewerWindow->getWindowHeightRaw(); if(!mDiff) { mDiff = new LLRenderTarget(); mDiff->allocate(width, height, GL_RGBA, false, false, LLTexUnit::TT_TEXTURE, true); } else if(mDiff->getWidth() != width || mDiff->getHeight() != height) { mDiff->resize(width, height, GL_RGBA); } mDiff->bindTarget(); mDiff->clear(); gTwoTextureCompareProgram.bind(); gGL.getTexUnit(0)->activate(); gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); gGL.getTexUnit(0)->bind(mFrames[0]); gGL.getTexUnit(0)->activate(); gGL.getTexUnit(1)->activate(); gGL.getTexUnit(1)->enable(LLTexUnit::TT_TEXTURE); gGL.getTexUnit(1)->bind(mFrames[1]); gGL.getTexUnit(1)->activate(); gl_rect_2d_simple_tex(width, height); mDiff->flush(); gTwoTextureCompareProgram.unbind(); gGL.getTexUnit(0)->disable(); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); gGL.getTexUnit(1)->disable(); gGL.getTexUnit(1)->unbind(LLTexUnit::TT_TEXTURE); mHasNewDiff = TRUE; //send out the query request. queryDiff(); } void LLSceneMonitor::queryDiff() { if(mDebugViewerVisible) { return; } calcDiffAggregate(); } //calculate Diff aggregate information in GPU, and enable gl occlusion query to capture it. void LLSceneMonitor::calcDiffAggregate() { if(!mHasNewDiff && !mDebugViewerVisible) { return; } if(!mQueryObject) { mQueryObject = get_new_occlusion_query_object_name(); } LLGLDepthTest depth(true, false, GL_ALWAYS); if(!mDebugViewerVisible) { glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); } LLGLSLShader* cur_shader = NULL; cur_shader = LLGLSLShader::sCurBoundShaderPtr; gOneTextureFilterProgram.bind(); gOneTextureFilterProgram.uniform1f("tolerance", mDiffTolerance); if(mHasNewDiff) { glBeginQueryARB(GL_SAMPLES_PASSED_ARB, mQueryObject); } gl_draw_scaled_target(0, 0, S32(mDiff->getWidth() * mDiffPixelRatio), S32(mDiff->getHeight() * mDiffPixelRatio), mDiff); if(mHasNewDiff) { glEndQueryARB(GL_SAMPLES_PASSED_ARB); mHasNewDiff = FALSE; mHasNewQueryResult = TRUE; } gOneTextureFilterProgram.unbind(); if(cur_shader != NULL) { cur_shader->bind(); } if(!mDebugViewerVisible) { glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); } } void LLSceneMonitor::fetchQueryResult() { if(!mHasNewQueryResult) { return; } mHasNewQueryResult = FALSE; GLuint available = 0; glGetQueryObjectuivARB(mQueryObject, GL_QUERY_RESULT_AVAILABLE_ARB, &available); if(!available) { return; } GLuint count = 0; glGetQueryObjectuivARB(mQueryObject, GL_QUERY_RESULT_ARB, &count); mDiffResult = count * 0.5f / (mDiff->getWidth() * mDiff->getHeight() * mDiffPixelRatio * mDiffPixelRatio); //0.5 -> (front face + back face) if(mDiffResult > 0.01f) { mRecording->extend(); } //llinfos << count << " : " << mDiffResult << llendl; } //------------------------------------------------------------------------------------------------------------- //definition of class LLSceneMonitorView //------------------------------------------------------------------------------------------------------------- LLSceneMonitorView::LLSceneMonitorView(const LLRect& rect) : LLFloater(LLSD()) { setRect(rect); setVisible(FALSE); setCanMinimize(false); setCanClose(true); } void LLSceneMonitorView::onClickCloseBtn() { setVisible(false); } void LLSceneMonitorView::setVisible(BOOL visible) { LLSceneMonitor::getInstance()->setDebugViewerVisible(visible); LLView::setVisible(visible); } void LLSceneMonitorView::draw() { const LLRenderTarget* target = LLSceneMonitor::getInstance()->getDiffTarget(); if(!target) { return; } F32 ratio = LLSceneMonitor::getInstance()->getDiffPixelRatio(); S32 height = (S32)(target->getHeight() * ratio); S32 width = (S32)(target->getWidth() * ratio); //S32 height = (S32) (gViewerWindow->getWindowRectScaled().getHeight()*0.5f); //S32 width = (S32) (gViewerWindow->getWindowRectScaled().getWidth() * 0.5f); LLRect new_rect; new_rect.setLeftTopAndSize(getRect().mLeft, getRect().mTop, width, height); setRect(new_rect); //draw background gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, LLColor4(0.f, 0.f, 0.f, 0.25f)); LLSceneMonitor::getInstance()->calcDiffAggregate(); //show some texts LLColor4 color = LLColor4::white; S32 line_height = LLFontGL::getFontMonospace()->getLineHeight(); S32 lines = 0; std::string num_str = llformat("Frame difference: %.6f", LLSceneMonitor::getInstance()->getDiffResult()); LLFontGL::getFontMonospace()->renderUTF8(num_str, 0, 5, getRect().getHeight() - line_height * lines, color, LLFontGL::LEFT, LLFontGL::TOP); lines++; num_str = llformat("Pixel tolerance: (R+G+B) < %.4f", LLSceneMonitor::getInstance()->getDiffTolerance()); LLFontGL::getFontMonospace()->renderUTF8(num_str, 0, 5, getRect().getHeight() - line_height * lines, color, LLFontGL::LEFT, LLFontGL::TOP); lines++; num_str = llformat("Sampling time: %.3f seconds", LLSceneMonitor::getInstance()->getSamplingTime()); LLFontGL::getFontMonospace()->renderUTF8(num_str, 0, 5, getRect().getHeight() - line_height * lines, color, LLFontGL::LEFT, LLFontGL::TOP); lines++; num_str = llformat("Scene Loading time: %.3f seconds", (F32)LLSceneMonitor::getInstance()->getRecording()->getAcceptedRecording().getDuration().value()); LLFontGL::getFontMonospace()->renderUTF8(num_str, 0, 5, getRect().getHeight() - line_height * lines, color, LLFontGL::LEFT, LLFontGL::TOP); lines++; LLView::draw(); }