# Conflicts:
#	indra/llaudio/llaudiodecodemgr.cpp
#	indra/llwindow/llwindowwin32.cpp
#	indra/newview/llperfstats.cpp
#	indra/newview/llperfstats.h
#	indra/newview/llvoavatar.cpp
#	indra/newview/llvoavatar.h
#	indra/newview/pipeline.cpp
master
Ansariel 2023-05-12 12:06:57 +02:00
commit 8e9b3a8f37
25 changed files with 339 additions and 463 deletions

View File

@ -1410,6 +1410,7 @@ Sovereign Engineer
SL-18497
SL-18525
SL-18534
SL-19690
SpacedOut Frye
VWR-34
VWR-45

View File

@ -133,7 +133,7 @@ MACRO(LL_ADD_PROJECT_UNIT_TESTS project sources)
if (DARWIN)
# test binaries always need to be signed for local development
set_target_properties(PROJECT_${project}_TEST_${name}
PROPERTIES
PROPERTIES
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "-")
endif ()
@ -236,7 +236,7 @@ FUNCTION(LL_ADD_INTEGRATION_TEST
# test binaries always need to be signed for local development
set_target_properties(INTEGRATION_TEST_${testname}
PROPERTIES
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "-")
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "-")
endif ()
# Add link deps to the executable

View File

@ -612,43 +612,40 @@ void LLAudioDecodeMgr::Impl::startMoreDecodes()
// Kick off a decode
mDecodes[decode_id] = LLPointer<LLVorbisDecodeState>(NULL);
try
{
main_queue->postTo(
general_queue,
[decode_id]() // Work done on general queue
bool posted = main_queue->postTo(
general_queue,
[decode_id]() // Work done on general queue
{
LLPointer<LLVorbisDecodeState> decode_state = beginDecodingAndWritingAudio(decode_id);
if (!decode_state)
{
LLPointer<LLVorbisDecodeState> decode_state = beginDecodingAndWritingAudio(decode_id);
if (gAudiop)
gAudiop->markSoundCorrupt(decode_id);
if (!decode_state)
{
if (gAudiop)
gAudiop->markSoundCorrupt(decode_id);
// Audio decode has errored
return decode_state;
}
// Disk write of decoded audio is now in progress off-thread
// Audio decode has errored
return decode_state;
},
[decode_id, this](LLPointer<LLVorbisDecodeState> decode_state) // Callback to main thread
mutable {
if (!gAudiop)
{
// There is no LLAudioEngine anymore. This might happen if
// an audio decode is enqueued just before shutdown.
return;
}
}
// At this point, we can be certain that the pointer to "this"
// is valid because the lifetime of "this" is dependent upon
// the lifetime of gAudiop.
// Disk write of decoded audio is now in progress off-thread
return decode_state;
},
[decode_id, this](LLPointer<LLVorbisDecodeState> decode_state) // Callback to main thread
mutable {
if (!gAudiop)
{
// There is no LLAudioEngine anymore. This might happen if
// an audio decode is enqueued just before shutdown.
return;
}
enqueueFinishAudio(decode_id, decode_state);
});
}
catch (const LLThreadSafeQueueInterrupt&)
// At this point, we can be certain that the pointer to "this"
// is valid because the lifetime of "this" is dependent upon
// the lifetime of gAudiop.
enqueueFinishAudio(decode_id, decode_state);
});
if (! posted)
{
// Shutdown
// Consider making processQueue() do a cleanup instead

View File

@ -146,7 +146,7 @@ S32 LLQueuedThread::updateQueue(F32 max_time_ms)
// schedule a call to threadedUpdate for every call to updateQueue
if (!isQuitting())
{
mRequestQueue.postIfOpen([=]()
mRequestQueue.post([=]()
{
LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("qt - update");
mIdleThread = false;

View File

@ -161,12 +161,7 @@ bool LL::WorkQueue::done()
return mQueue.done();
}
void LL::WorkQueue::post(const Work& callable)
{
mQueue.push(callable);
}
bool LL::WorkQueue::postIfOpen(const Work& callable)
bool LL::WorkQueue::post(const Work& callable)
{
return mQueue.pushIfOpen(callable);
}
@ -215,26 +210,16 @@ bool LL::WorkSchedule::done()
return mQueue.done();
}
void LL::WorkSchedule::post(const Work& callable)
bool LL::WorkSchedule::post(const Work& callable)
{
// Use TimePoint::clock::now() instead of TimePoint's representation of
// the epoch because this WorkSchedule may contain a mix of past-due
// TimedWork items and TimedWork items scheduled for the future. Sift this
// new item into the correct place.
post(callable, TimePoint::clock::now());
return post(callable, TimePoint::clock::now());
}
void LL::WorkSchedule::post(const Work& callable, const TimePoint& time)
{
mQueue.push(TimedWork(time, callable));
}
bool LL::WorkSchedule::postIfOpen(const Work& callable)
{
return postIfOpen(callable, TimePoint::clock::now());
}
bool LL::WorkSchedule::postIfOpen(const Work& callable, const TimePoint& time)
bool LL::WorkSchedule::post(const Work& callable, const TimePoint& time)
{
return mQueue.pushIfOpen(TimedWork(time, callable));
}

View File

@ -83,13 +83,10 @@ namespace LL
/*---------------------- fire and forget API -----------------------*/
/// fire-and-forget
virtual void post(const Work&) = 0;
/**
* post work, unless the queue is closed before we can post
*/
virtual bool postIfOpen(const Work&) = 0;
virtual bool post(const Work&) = 0;
/**
* post work, unless the queue is full
@ -247,13 +244,10 @@ namespace LL
/*---------------------- fire and forget API -----------------------*/
/// fire-and-forget
void post(const Work&) override;
/**
* post work, unless the queue is closed before we can post
*/
bool postIfOpen(const Work&) override;
bool post(const Work&) override;
/**
* post work, unless the queue is full
@ -320,22 +314,16 @@ namespace LL
/*---------------------- fire and forget API -----------------------*/
/// fire-and-forget
void post(const Work& callable) override;
/// fire-and-forget, but at a particular (future?) time
void post(const Work& callable, const TimePoint& time);
/**
* post work, unless the queue is closed before we can post
*/
bool postIfOpen(const Work& callable) override;
bool post(const Work& callable) override;
/**
* post work for a particular time, unless the queue is closed before
* we can post
*/
bool postIfOpen(const Work& callable, const TimePoint& time);
bool post(const Work& callable, const TimePoint& time);
/**
* post work, unless the queue is full
@ -356,7 +344,7 @@ namespace LL
* an LLCond variant, e.g. LLOneShotCond or LLBoolCond.
*/
template <typename Rep, typename Period, typename CALLABLE>
void postEvery(const std::chrono::duration<Rep, Period>& interval,
bool postEvery(const std::chrono::duration<Rep, Period>& interval,
CALLABLE&& callable);
private:
@ -417,15 +405,10 @@ namespace LL
// move-only callable; but naturally this statement must be
// the last time we reference this instance, which may become
// moved-from.
try
{
auto target{ std::dynamic_pointer_cast<WorkSchedule>(mTarget.lock()) };
target->post(std::move(*this), mStart);
}
catch (const Closed&)
{
// Once this queue is closed, oh well, just stop
}
auto target{ std::dynamic_pointer_cast<WorkSchedule>(mTarget.lock()) };
// Discard bool return: once this queue is closed, oh well,
// just stop
target->post(std::move(*this), mStart);
}
}
@ -437,7 +420,7 @@ namespace LL
};
template <typename Rep, typename Period, typename CALLABLE>
void WorkSchedule::postEvery(const std::chrono::duration<Rep, Period>& interval,
bool WorkSchedule::postEvery(const std::chrono::duration<Rep, Period>& interval,
CALLABLE&& callable)
{
if (interval.count() <= 0)
@ -454,7 +437,7 @@ namespace LL
// Instantiate and post a suitable BackJack, binding a weak_ptr to
// self, the current time, the desired interval and the desired
// callable.
post(
return post(
BackJack<Rep, Period, CALLABLE>(
getWeak(), TimePoint::clock::now(), interval, std::move(callable)));
}
@ -516,7 +499,7 @@ namespace LL
// Here we believe target WorkQueue still exists. Post to it a
// lambda that packages our callable, our callback and a weak_ptr
// to this originating WorkQueue.
tptr->post(
return tptr->post(
[reply = super::getWeak(),
callable = std::move(callable),
callback = std::move(callback)]
@ -547,9 +530,6 @@ namespace LL
},
// if caller passed a TimePoint, pass it along to post()
std::forward<ARGS>(args)...);
// looks like we were able to post()
return true;
}
template <typename... ARGS>
@ -560,18 +540,9 @@ namespace LL
auto tptr = target.lock();
if (tptr)
{
try
{
tptr->post(std::forward<ARGS>(args)...);
// we were able to post()
return true;
}
catch (const Closed&)
{
// target WorkQueue still exists, but is Closed
}
return tptr->post(std::forward<ARGS>(args)...);
}
// either target no longer exists, or its WorkQueue is Closed
// target no longer exists
return false;
}
@ -583,7 +554,7 @@ namespace LL
auto operator()(WorkQueueBase* self, CALLABLE&& callable, ARGS&&... args)
{
LLCoros::Promise<RETURNTYPE> promise;
self->post(
bool posted = self->post(
// We dare to bind a reference to Promise because it's
// specifically designed for cross-thread communication.
[&promise, callable = std::move(callable)]()
@ -600,6 +571,10 @@ namespace LL
},
// if caller passed a TimePoint, pass it to post()
std::forward<ARGS>(args)...);
if (! posted)
{
LLTHROW(WorkQueueBase::Closed());
}
auto future{ LLCoros::getFuture(promise) };
// now, on the calling thread, wait for that result
LLCoros::TempStatus st("waiting for WorkQueue::waitForResult()");
@ -615,7 +590,7 @@ namespace LL
void operator()(WorkQueueBase* self, CALLABLE&& callable, ARGS&&... args)
{
LLCoros::Promise<void> promise;
self->post(
bool posted = self->post(
// &promise is designed for cross-thread access
[&promise, callable = std::move(callable)]()
mutable {
@ -631,6 +606,10 @@ namespace LL
},
// if caller passed a TimePoint, pass it to post()
std::forward<ARGS>(args)...);
if (! posted)
{
LLTHROW(WorkQueueBase::Closed());
}
auto future{ LLCoros::getFuture(promise) };
// block until set_value()
LLCoros::TempStatus st("waiting for void WorkQueue::waitForResult()");

View File

@ -93,13 +93,18 @@ LLImageDecodeThread::handle_t LLImageDecodeThread::decodeImage(
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
// Instantiate the ImageRequest right in the lambda, why not?
mThreadPool->getQueue().post(
bool posted = mThreadPool->getQueue().post(
[req = ImageRequest(image, discard, needs_aux, responder)]
() mutable
{
auto done = req.processRequest();
req.finishRequest(done);
});
if (! posted)
{
LL_DEBUGS() << "Tried to start decoding on shutdown" << LL_ENDL;
// should this return 0?
}
// It's important to our consumer (LLTextureFetchWorker) that we return a
// nonzero handle. It is NOT important that the nonzero handle be unique:

View File

@ -343,7 +343,7 @@ public:
template <typename CALLABLE>
bool post(CALLABLE&& func)
{
return getQueue().postIfOpen(std::forward<CALLABLE>(func));
return getQueue().post(std::forward<CALLABLE>(func));
}
void run() override;

View File

@ -373,16 +373,10 @@ struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool
template <typename CALLABLE>
void post(CALLABLE&& func)
{
try
{
LL_DEBUGS("Window") << "post( callable ) to work queue" << LL_ENDL; // <FS:Beq/> extra debug for threaded window handler
getQueue().post(std::forward<CALLABLE>(func));
}
catch (const LLThreadSafeQueueInterrupt&)
{
// Shutdown timing is tricky. The main thread can end up trying
// to post a cursor position after having closed the WorkQueue.
}
// Ignore bool return. Shutdown timing is tricky: the main thread can
// end up trying to post a cursor position after having closed the
// WorkQueue.
getQueue().post(std::forward<CALLABLE>(func));
}
/**
@ -2345,13 +2339,8 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
ASSERT_WINDOW_THREAD();
LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32;
LL_DEBUGS("Window") << "mainWindowProc(" << std::hex << h_wnd
<< ", " << u_msg
<< ", " << w_param << ")" << std::dec << LL_ENDL;
if (u_msg == WM_POST_FUNCTION_)
{
LL_DEBUGS("Window") << "WM_POST_FUNCTION_" << LL_ENDL;
// from LLWindowWin32Thread::Post()
// Cast l_param back to the pointer to the heap FuncType
// allocated by Post(). Capture in unique_ptr so we'll delete
@ -2398,8 +2387,6 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
case WM_DEVICECHANGE:
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_DEVICECHANGE");
LL_INFOS("Window") << " WM_DEVICECHANGE: wParam=" << w_param
<< "; lParam=" << l_param << LL_ENDL;
if (w_param == DBT_DEVNODES_CHANGED || w_param == DBT_DEVICEARRIVAL)
{
WINDOW_IMP_POST(window_imp->mCallbacks->handleDeviceChange(window_imp));
@ -2461,13 +2448,6 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
{
// This message should be sent whenever the app gains or loses focus.
BOOL activating = (BOOL)w_param;
BOOL minimized = window_imp->getMinimized();
LL_INFOS("Window") << "WINDOWPROC ActivateApp "
<< " activating " << S32(activating)
<< " minimized " << S32(minimized)
<< " fullscreen " << S32(window_imp->mFullscreen)
<< LL_ENDL;
if (window_imp->mFullscreen)
{
@ -2502,20 +2482,10 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
// Can be one of WA_ACTIVE, WA_CLICKACTIVE, or WA_INACTIVE
BOOL activating = (LOWORD(w_param) != WA_INACTIVE);
BOOL minimized = BOOL(HIWORD(w_param));
if (!activating && LLWinImm::isAvailable() && window_imp->mPreeditor)
{
window_imp->interruptLanguageTextInput();
}
// JC - I'm not sure why, but if we don't report that we handled the
// WM_ACTIVATE message, the WM_ACTIVATEAPP messages don't work
// properly when we run fullscreen.
LL_INFOS("Window") << "WINDOWPROC Activate "
<< " activating " << S32(activating)
<< " minimized " << S32(minimized)
<< LL_ENDL;
});
break;
@ -2593,13 +2563,7 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
window_imp->mRawWParam = w_param;
window_imp->mRawLParam = l_param;
{
LL_INFOS("Window") << "Debug WindowProc WM_KEYDOWN "
<< " key " << S32(w_param)
<< LL_ENDL;
gKeyboard->handleKeyDown(w_param, mask);
}
gKeyboard->handleKeyDown(w_param, mask);
});
if (eat_keystroke) return 0; // skip DefWindowProc() handling if we're consuming the keypress
break;
@ -2619,11 +2583,7 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
window_imp->mRawLParam = l_param;
{
LL_RECORD_BLOCK_TIME(FTM_KEYHANDLER);
LL_INFOS("Window") << "Debug WindowProc WM_KEYUP "
<< " key " << S32(w_param)
<< LL_ENDL;
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_KEYUP");
gKeyboard->handleKeyUp(w_param, mask);
}
});
@ -2633,7 +2593,6 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
case WM_IME_SETCONTEXT:
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_IME_SETCONTEXT");
LL_INFOS("Window") << "WM_IME_SETCONTEXT" << LL_ENDL;
if (LLWinImm::isAvailable() && window_imp->mPreeditor)
{
l_param &= ~ISC_SHOWUICOMPOSITIONWINDOW;
@ -2644,7 +2603,6 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
case WM_IME_STARTCOMPOSITION:
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_IME_STARTCOMPOSITION");
LL_INFOS("Window") << "WM_IME_STARTCOMPOSITION" << LL_ENDL;
if (LLWinImm::isAvailable() && window_imp->mPreeditor)
{
WINDOW_IMP_POST(window_imp->handleStartCompositionMessage());
@ -2655,7 +2613,6 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
case WM_IME_ENDCOMPOSITION:
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_IME_ENDCOMPOSITION");
LL_INFOS("Window") << "WM_IME_ENDCOMPOSITION" << LL_ENDL;
if (LLWinImm::isAvailable() && window_imp->mPreeditor)
{
return 0;
@ -2665,7 +2622,6 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
case WM_IME_COMPOSITION:
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_IME_COMPOSITION");
LL_INFOS("Window") << "WM_IME_COMPOSITION" << LL_ENDL;
if (LLWinImm::isAvailable() && window_imp->mPreeditor)
{
WINDOW_IMP_POST(window_imp->handleCompositionMessage(l_param));
@ -2676,7 +2632,6 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
case WM_IME_REQUEST:
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_IME_REQUEST");
LL_INFOS("Window") << "WM_IME_REQUEST" << LL_ENDL;
if (LLWinImm::isAvailable() && window_imp->mPreeditor)
{
LRESULT result;
@ -2705,9 +2660,7 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
// it is worth trying. The good old WM_CHAR works just fine even for supplementary
// characters. We just need to take care of surrogate pairs sent as two WM_CHAR's
// by ourselves. It is not that tough. -- Alissa Sabre @ SL
LL_INFOS("Window") << "Debug WindowProc WM_CHAR "
<< " key " << S32(w_param)
<< LL_ENDL;
// Even if LLWindowCallbacks::handleUnicodeChar(llwchar, BOOL) returned FALSE,
// we *did* processed the event, so I believe we should not pass it to DefWindowProc...
window_imp->handleUnicodeUTF16((U16)w_param, gKeyboard->currentMask(FALSE));
@ -3031,21 +2984,6 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_SIZE");
window_imp->updateWindowRect();
S32 width = S32(LOWORD(l_param));
S32 height = S32(HIWORD(l_param));
LL_INFOS("Window");
BOOL maximized = (w_param == SIZE_MAXIMIZED);
BOOL restored = (w_param == SIZE_RESTORED);
BOOL minimized = (w_param == SIZE_MINIMIZED);
LL_CONT << "WINDOWPROC Size "
<< width << "x" << height
<< " max " << S32(maximized)
<< " min " << S32(minimized)
<< " rest " << S32(restored);
LL_ENDL;
// There's an odd behavior with WM_SIZE that I would call a bug. If
// the window is maximized, and you call MoveWindow() with a size smaller
@ -3111,7 +3049,6 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
case WM_SETFOCUS:
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_SETFOCUS");
LL_INFOS("Window") << "WINDOWPROC SetFocus" << LL_ENDL;
// <FS:Ansariel> Stop flashing when we gain focus
if (window_imp->mWindowHandle)
@ -3133,7 +3070,6 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
case WM_KILLFOCUS:
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_KILLFOCUS");
LL_INFOS("Window") << "WINDOWPROC KillFocus" << LL_ENDL;
WINDOW_IMP_POST(window_imp->mCallbacks->handleFocusLost(window_imp));
return 0;
}
@ -3254,7 +3190,7 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
default:
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - default");
LL_INFOS("Window") << "Unhandled windows message code: 0x" << std::hex << U32(u_msg) << LL_ENDL;
LL_DEBUGS("Window") << "Unhandled windows message code: 0x" << std::hex << U32(u_msg) << LL_ENDL;
}
break;
}

View File

@ -756,14 +756,7 @@ void FSFloaterPerformance::populateNearbyList()
row[colno]["column"] = "art_value";
row[colno]["type"] = "text";
if (is_slow)
{
row[colno]["value"] = llformat( "%.2f", LLPerfStats::raw_to_us( avatar->getLastART() ) );
}
else
{
row[colno]["value"] = llformat( "%.2f", render_av_gpu_ms * 1000.f);
}
row[colno]["value"] = llformat( "%.2f", render_av_gpu_ms * 1000.f);
row[colno]["font"]["name"] = "SANSSERIF";
row[colno]["width"] = "50";
colno++;

View File

@ -5522,6 +5522,8 @@ void LLAppViewer::idle()
LLFrameTimer::updateFrameTime();
LLFrameTimer::updateFrameCount();
LLEventTimer::updateClass();
LLPerfStats::updateClass();
// LLApp::stepFrame() performs the above three calls plus mRunner.run().
// Not sure why we don't call stepFrame() here, except that LLRunner seems
// completely redundant with LLEventTimer.

View File

@ -349,15 +349,18 @@ public:
void resetDrawOrders() { }
static void applyModelMatrix(const LLDrawInfo& params);
virtual void pushBatches(U32 type, bool texture = true, bool batch_textures = false);
virtual void pushRiggedBatches(U32 type, bool texture = true, bool batch_textures = false);
// Use before a non-GLTF batch if it is interleaved with GLTF batches that share the same shader
static void resetGLTFTextureTransform();
void pushBatches(U32 type, bool texture = true, bool batch_textures = false);
void pushRiggedBatches(U32 type, bool texture = true, bool batch_textures = false);
void pushGLTFBatches(U32 type);
void pushGLTFBatch(LLDrawInfo& params);
void pushRiggedGLTFBatches(U32 type);
void pushRiggedGLTFBatch(LLDrawInfo& params, LLVOAvatar*& lastAvatar, U64& lastMeshId);
virtual void pushMaskBatches(U32 type, bool texture = true, bool batch_textures = false);
virtual void pushRiggedMaskBatches(U32 type, bool texture = true, bool batch_textures = false);
virtual void pushBatch(LLDrawInfo& params, bool texture, bool batch_textures = false);
void pushMaskBatches(U32 type, bool texture = true, bool batch_textures = false);
void pushRiggedMaskBatches(U32 type, bool texture = true, bool batch_textures = false);
void pushBatch(LLDrawInfo& params, bool texture, bool batch_textures = false);
void pushBumpBatch(LLDrawInfo& params, bool texture, bool batch_textures = false);
static bool uploadMatrixPalette(LLDrawInfo& params);
static bool uploadMatrixPalette(LLVOAvatar* avatar, LLMeshSkinInfo* skinInfo);
virtual void renderGroup(LLSpatialGroup* group, U32 type, bool texture = true);

View File

@ -158,7 +158,7 @@ void LLDrawPoolAlpha::renderPostDeferred(S32 pass)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL;
if ((!LLPipeline::sRenderTransparentWater || gCubeSnapshot) && getType() == LLDrawPool::POOL_ALPHA_PRE_WATER)
if (LLPipeline::isWaterClip() && getType() == LLDrawPool::POOL_ALPHA_PRE_WATER)
{ // don't render alpha objects on the other side of the water plane if water is opaque
return;
}

View File

@ -77,6 +77,7 @@ static LLGLSLShader* shader = NULL;
static S32 cube_channel = -1;
static S32 diffuse_channel = -1;
static S32 bump_channel = -1;
static BOOL shiny = FALSE;
// Enabled after changing LLViewerTexture::mNeedsCreateTexture to an
// LLAtomicBool; this should work just fine, now. HB
@ -201,7 +202,7 @@ void LLStandardBumpmap::destroyGL()
LLDrawPoolBump::LLDrawPoolBump()
: LLRenderPass(LLDrawPool::POOL_BUMP)
{
mShiny = FALSE;
shiny = FALSE;
}
@ -350,7 +351,7 @@ void LLDrawPoolBump::beginFullbrightShiny()
diffuse_channel = 0;
}
mShiny = TRUE;
shiny = TRUE;
}
void LLDrawPoolBump::renderFullbrightShiny()
@ -402,7 +403,7 @@ void LLDrawPoolBump::endFullbrightShiny()
diffuse_channel = -1;
cube_channel = 0;
mShiny = FALSE;
shiny = FALSE;
}
void LLDrawPoolBump::renderGroup(LLSpatialGroup* group, U32 type, bool texture = true)
@ -545,7 +546,7 @@ void LLDrawPoolBump::renderDeferred(S32 pass)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; //LL_RECORD_BLOCK_TIME(FTM_RENDER_BUMP);
mShiny = TRUE;
shiny = TRUE;
for (int i = 0; i < 2; ++i)
{
bool rigged = i == 1;
@ -579,11 +580,11 @@ void LLDrawPoolBump::renderDeferred(S32 pass)
avatar = params.mAvatar;
skin = params.mSkinInfo->mHash;
}
pushBatch(params, true, false);
pushBumpBatch(params, true, false);
}
else
{
pushBatch(params, true, false);
pushBumpBatch(params, true, false);
}
}
@ -593,7 +594,7 @@ void LLDrawPoolBump::renderDeferred(S32 pass)
gGL.getTexUnit(0)->activate();
}
mShiny = FALSE;
shiny = FALSE;
}
@ -1221,12 +1222,12 @@ void LLDrawPoolBump::pushBumpBatches(U32 type)
}
}
}
pushBatch(params, false);
pushBumpBatch(params, false);
}
}
}
void LLDrawPoolBump::pushBatch(LLDrawInfo& params, bool texture, bool batch_textures)
void LLRenderPass::pushBumpBatch(LLDrawInfo& params, bool texture, bool batch_textures)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL;
applyModelMatrix(params);
@ -1247,7 +1248,7 @@ void LLDrawPoolBump::pushBatch(LLDrawInfo& params, bool texture, bool batch_text
{ //not batching textures or batch has only 1 texture -- might need a texture matrix
if (params.mTextureMatrix)
{
if (mShiny)
if (shiny)
{
gGL.getTexUnit(0)->activate();
gGL.matrixMode(LLRender::MM_TEXTURE);
@ -1266,7 +1267,7 @@ void LLDrawPoolBump::pushBatch(LLDrawInfo& params, bool texture, bool batch_text
tex_setup = true;
}
if (mShiny && mShaderLevel > 1 && texture)
if (shiny && mShaderLevel > 1 && texture)
{
if (params.mTexture.notNull())
{
@ -1284,7 +1285,7 @@ void LLDrawPoolBump::pushBatch(LLDrawInfo& params, bool texture, bool batch_text
if (tex_setup)
{
if (mShiny)
if (shiny)
{
gGL.getTexUnit(0)->activate();
}

View File

@ -53,7 +53,6 @@ public:
LLDrawPoolBump();
/*virtual*/ void prerender() override;
void pushBatch(LLDrawInfo& params, bool texture, bool batch_textures = false) override;
void pushBumpBatches(U32 type);
void renderGroup(LLSpatialGroup* group, U32 type, bool texture) override;

View File

@ -610,13 +610,6 @@ void renderFace(LLDrawable* drawable, LLFace *face)
LLVOVolume* vobj = drawable->getVOVolume();
if (vobj)
{
// <FS:Beq> Placeholder - This function emits drawcalls but is only used in one place and not useful for stats.
// TODO(Beq) if we need this consider moving it to llSelectMgr loop instead to reduce overhead.
// std::unique_ptr<FSPerfStats::RecordAttachmentTime> ratPtr{};
// if(vobj->isAttachment())
// {
// trackAttachments(vobj, LLPipeline::sShadowRender, &ratPtr);
// }
LLVolume* volume = NULL;
if (drawable->isState(LLDrawable::RIGGED))

View File

@ -456,15 +456,8 @@ void LLFloaterPerformance::populateNearbyList()
row[1]["column"] = "complex_value";
row[1]["type"] = "text";
if (is_slow && !showTunedART)
{
row[1]["value"] = llformat( "%.f", LLPerfStats::raw_to_us( avatar->getLastART() ) );
}
else
{
// use GPU time in us
row[1]["value"] = llformat( "%.f", render_av_gpu_ms * 1000.f);
}
// use GPU time in us
row[1]["value"] = llformat( "%.f", render_av_gpu_ms * 1000.f);
row[1]["font"]["name"] = "SANSSERIF";
row[3]["column"] = "name";

View File

@ -39,6 +39,11 @@ extern LLControlGroup gSavedSettings;
namespace LLPerfStats
{
// avatar timing metrics in ms (updated once per mainloop iteration)
std::atomic<F32> sTotalAvatarTime = 0.f;
std::atomic<F32> sAverageAvatarTime = 0.f;
std::atomic<F32> sMaxAvatarTime = 0.f;
// <FS:Beq> extra profiling
#ifdef USAGE_TRACKING
std::atomic<int64_t> inUse{0};
@ -151,14 +156,12 @@ namespace LLPerfStats
resetChanges();
}
StatsRecorder::StatsRecorder():q(1024*16),t(&StatsRecorder::run)
StatsRecorder::StatsRecorder():q(1024*16)
{
// create a queue
// create a thread to consume from the queue
tunables.initialiseFromSettings();
LLPerfStats::cpu_hertz = (F64)LLTrace::BlockTimer::countsPerSecond();
LLPerfStats::vsync_max_fps = gViewerWindow->getWindow()->getRefreshRate();
t.detach();
}
// static
@ -182,11 +185,13 @@ namespace LLPerfStats
// RENDER_MESHREPO,
StatType_t::RENDER_IDLE };
#if 0
static constexpr std::initializer_list<StatType_t> avatarStatsToAvg = {
StatType_t::RENDER_GEOMETRY,
StatType_t::RENDER_SHADOWS,
StatType_t::RENDER_COMBINED,
StatType_t::RENDER_IDLE };
#endif
// <FS:Beq> restore FPSLimit reporting
// if( /*sceneStats[static_cast<size_t>(StatType_t::RENDER_FPSLIMIT)] != 0 ||*/ sceneStats[static_cast<size_t>(StatType_t::RENDER_SLEEP)] != 0 )
@ -212,20 +217,6 @@ namespace LLPerfStats
}
}
auto& statsMapAv = statsDoubleBuffer[writeBuffer][static_cast<size_t>(ObjType_t::OT_AVATAR)];
for(auto& stat_entry : statsMapAv)
{
for(auto& stat : avatarStatsToAvg)
{
auto val = stat_entry.second[static_cast<size_t>(stat)];
if(val > SMOOTHING_PERIODS)
{
auto avg = statsDoubleBuffer[writeBuffer ^ 1][static_cast<size_t>(ObjType_t::OT_AVATAR)][stat_entry.first][static_cast<size_t>(stat)];
stat_entry.second[static_cast<size_t>(stat)] = avg + (val / SMOOTHING_PERIODS) - (avg / SMOOTHING_PERIODS);
}
}
}
// swap the buffers
if(enabled())
{
@ -305,6 +296,37 @@ namespace LLPerfStats
}
}
// called once per main loop iteration on main thread
void updateClass()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
sTotalAvatarTime = LLVOAvatar::getTotalGPURenderTime();
sAverageAvatarTime = LLVOAvatar::getAverageGPURenderTime();
sMaxAvatarTime = LLVOAvatar::getMaxGPURenderTime();
auto general = LL::WorkQueue::getInstance("General");
if (general)
{
general->post([] { StatsRecorder::update(); });
}
}
// called once per main loop iteration on General thread
void StatsRecorder::update()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
StatsRecord upd;
auto& instance{ StatsRecorder::getInstance() };
//while (enabled() && !LLApp::isQuitting() && instance.q.tryPop(upd))
while (enabled() && !LLApp::isQuitting() && instance.q.try_dequeue(upd))
{
instance.processUpdate(upd);
}
}
//static
int StatsRecorder::countNearbyAvatars(S32 distance)
{
@ -339,6 +361,8 @@ namespace LLPerfStats
// static
void StatsRecorder::updateAvatarParams()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
if(tunables.autoTuneTimeout)
{
LLPerfStats::lastSleepedFrame = gFrameCount;
@ -392,10 +416,10 @@ namespace LLPerfStats
}
}
auto av_render_max_raw = LLPerfStats::StatsRecorder::getMax(ObjType_t::OT_AVATAR, LLPerfStats::StatType_t::RENDER_COMBINED);
auto av_render_max_raw = ms_to_raw(sMaxAvatarTime);
// Is our target frame time lower than current? If so we need to take action to reduce draw overheads.
// cumulative avatar time (includes idle processing, attachments and base av)
auto tot_avatar_time_raw = LLPerfStats::StatsRecorder::getSum(ObjType_t::OT_AVATAR, LLPerfStats::StatType_t::RENDER_COMBINED);
auto tot_avatar_time_raw = ms_to_raw(sTotalAvatarTime);
// The frametime budget we have based on the target FPS selected
auto target_frame_time_raw = (U64)llround(LLPerfStats::cpu_hertz / (target_fps == 0 ? 1 : target_fps));
@ -429,7 +453,7 @@ namespace LLPerfStats
// if so we've got work to do
// how much of the frame was spent on non avatar related work?
U64 non_avatar_time_raw = tot_frame_time_raw - tot_avatar_time_raw;
U64 non_avatar_time_raw = tot_frame_time_raw > tot_avatar_time_raw ? tot_frame_time_raw - tot_avatar_time_raw : 0;
// If the target frame time < scene time (estimated as non_avatar time)
U64 target_avatar_time_raw;
@ -486,7 +510,11 @@ namespace LLPerfStats
{
new_render_limit_ns = renderAvatarMaxART_ns;
}
new_render_limit_ns -= LLPerfStats::ART_MIN_ADJUST_DOWN_NANOS;
if (new_render_limit_ns > LLPerfStats::ART_MIN_ADJUST_DOWN_NANOS)
{
new_render_limit_ns -= LLPerfStats::ART_MIN_ADJUST_DOWN_NANOS;
}
// bounce at the bottom to prevent "no limit"
new_render_limit_ns = std::max((U64)new_render_limit_ns, (U64)LLPerfStats::ART_MINIMUM_NANOS);

View File

@ -64,6 +64,10 @@ namespace LLPerfStats
extern std::atomic<int64_t> inUseAttachmentUnRigged;
#endif
// </FS:Beq>
// called once per main loop iteration
void updateClass();
// Note if changing these, they should correspond with the log range of the correpsonding sliders
static constexpr U64 ART_UNLIMITED_NANOS{50000000};
static constexpr U64 ART_MINIMUM_NANOS{100000};
@ -90,7 +94,7 @@ namespace LLPerfStats
enum class ObjType_t{
OT_GENERAL=0, // Also Unknown. Used for n/a type stats such as scenery
OT_AVATAR,
OT_AVATAR, // <FS:Ansariel> Leave this in for now so I don't have to deal with the bugs in FSFloaterPerformance...
OT_COUNT
};
enum class StatType_t{
@ -186,6 +190,9 @@ namespace LLPerfStats
// </FS:Beq>
public:
// called once per main loop iteration on General thread
static void update();
static inline StatsRecorder& getInstance()
{
static StatsRecorder instance;
@ -269,7 +276,6 @@ namespace LLPerfStats
auto ot{upd.objType};
auto& key{upd.objID};
auto& avKey{upd.avID};
auto type {upd.statType};
auto val {upd.time};
// <FS:Beq> markup to support coverage testing on stats collection
@ -286,13 +292,6 @@ namespace LLPerfStats
doUpd(key, ot, type,val);
return;
}
if (ot == ObjType_t::OT_AVATAR)
{
// LL_INFOS("perfstats") << "Avatar update:" << LL_ENDL;
doUpd(avKey, ot, type, val);
return;
}
}
static inline void doUpd(const LLUUID& key, ObjType_t ot, StatType_t type, uint64_t val)
@ -321,47 +320,7 @@ namespace LLPerfStats
static void toggleBuffer();
static void clearStatsBuffers();
// thread entry
static void run()
{
StatsRecord upd[10];
auto & instance {StatsRecorder::getInstance()};
LL_PROFILER_SET_THREAD_NAME("PerfStats");
while( enabled() && !LLApp::isExiting() )
{
// <FS:Beq> We don't want these queues
// auto count = 0;
// while (count < 10)
// {
// if (instance.q.tryPopFor(std::chrono::milliseconds(10), upd[count]))
// {
// count++;
// }
// else
// {
// break;
// }
// }
// //LL_PROFILER_THREAD_BEGIN("PerfStats");
auto count = instance.q.wait_dequeue_bulk_timed(upd, 10, std::chrono::milliseconds(10));
LL_PROFILER_THREAD_BEGIN("PerfStats");
// </FS:Beq>
if(count)
{
// LL_INFOS("perfstats") << "processing " << count << " updates." << LL_ENDL;
for(auto i =0; i < count; i++)
{
instance.processUpdate(upd[i]);
}
}
LL_PROFILER_THREAD_END("PerfStats"); // <FS:Beq/>
}
}
Queue q;
std::thread t;
~StatsRecorder() = default;
StatsRecorder(const StatsRecorder&) = delete;
@ -388,27 +347,27 @@ namespace LLPerfStats
// LL_PROFILE_ZONE_COLOR(tracy::Color::Orange);
LL_PROFILE_ZONE_COLOR(tracy::Color::Orange);
#ifdef USAGE_TRACKING
if(stat.objType == FSPerfStats::ObjType_t::OT_ATTACHMENT)
if(stat.objType == LLPerfStats::ObjType_t::OT_ATTACHMENT)
{
LL_PROFILE_PLOT_CONFIG_SQUARE("InUse");
LL_PROFILE_PLOT_CONFIG_SQUARE("InUseAttachment");
LL_PROFILE_PLOT_CONFIG_SQUARE("InUseAttachmentRigged");
LL_PROFILE_PLOT_CONFIG_SQUARE("InUseAttachmentUnRigged");
if(!stat.isRigged && FSPerfStats::inUseAvatar){LL_PROFILE_ZONE_TEXT("OVERLAP AVATAR",14);}
FSPerfStats::inUse++;
LL_PROFILE_PLOT("InUse", (int64_t)FSPerfStats::inUse);
FSPerfStats::inUseAttachment++;
LL_PROFILE_PLOT("InUseAttachment", (int64_t)FSPerfStats::inUseAttachment);
if(!stat.isRigged && LLPerfStats::inUseAvatar){LL_PROFILE_ZONE_TEXT("OVERLAP AVATAR",14);}
LLPerfStats::inUse++;
LL_PROFILE_PLOT("InUse", (int64_t)LLPerfStats::inUse);
LLPerfStats::inUseAttachment++;
LL_PROFILE_PLOT("InUseAttachment", (int64_t)LLPerfStats::inUseAttachment);
if (stat.isRigged)
{
FSPerfStats::inUseAttachmentRigged++;
LL_PROFILE_PLOT("InUseAttachmentRigged", (int64_t)FSPerfStats::inUseAttachmentRigged);
LLPerfStats::inUseAttachmentRigged++;
LL_PROFILE_PLOT("InUseAttachmentRigged", (int64_t)LLPerfStats::inUseAttachmentRigged);
}
else
{
FSPerfStats::inUseAttachmentUnRigged++;
LL_PROFILE_PLOT("InUseAttachmentUnRigged", (int64_t)FSPerfStats::inUseAttachmentUnRigged);
LLPerfStats::inUseAttachmentUnRigged++;
LL_PROFILE_PLOT("InUseAttachmentUnRigged", (int64_t)LLPerfStats::inUseAttachmentUnRigged);
}
}
#endif
@ -430,23 +389,6 @@ namespace LLPerfStats
// </FS:Beq>
};
template < ObjType_t OD = ObjTypeDiscriminator,
std::enable_if_t<OD == ObjType_t::OT_AVATAR> * = nullptr>
RecordTime( const LLUUID & av, StatType_t type ):RecordTime<ObjTypeDiscriminator>(std::move(av), LLUUID::null, type)
{
// <FS:Beq> extra profiling coverage tracking
// LL_PROFILE_ZONE_COLOR(tracy::Color::Purple)
LL_PROFILE_ZONE_COLOR(tracy::Color::Purple);
#ifdef USAGE_TRACKING
if(LLPerfStats::inUseAvatar){LL_PROFILE_ZONE_TEXT("OVERLAP AVATAR",14);}
FSPerfStats::inUseAvatar++;
LL_PROFILE_PLOT("InUseAv", (int64_t)LLPerfStats::inUseAvatar);
LLPerfStats::inUse++;
LL_PROFILE_PLOT("InUse", (int64_t)LLPerfStats::inUse);
#endif
// </FS:Beq>
};
~RecordTime()
{
if(!LLPerfStats::StatsRecorder::enabled())
@ -458,32 +400,32 @@ namespace LLPerfStats
// <FS:Beq> extra profiling coverage tracking
#ifdef USAGE_TRACKING
--FSPerfStats::inUse;
LL_PROFILE_PLOT("InUse", (int64_t)FSPerfStats::inUse);
if (stat.objType == FSPerfStats::ObjType_t::OT_ATTACHMENT)
--LLPerfStats::inUse;
LL_PROFILE_PLOT("InUse", (int64_t)LLPerfStats::inUse);
if (stat.objType == LLPerfStats::ObjType_t::OT_ATTACHMENT)
{
--FSPerfStats::inUseAttachment;
LL_PROFILE_PLOT("InUseAttachment", (int64_t)FSPerfStats::inUseAttachment);
--LLPerfStats::inUseAttachment;
LL_PROFILE_PLOT("InUseAttachment", (int64_t)LLPerfStats::inUseAttachment);
if (stat.isRigged)
{
--FSPerfStats::inUseAttachmentRigged;
LL_PROFILE_PLOT("InUseAttachmentRigged", (int64_t)FSPerfStats::inUseAttachmentRigged);
--LLPerfStats::inUseAttachmentRigged;
LL_PROFILE_PLOT("InUseAttachmentRigged", (int64_t)LLPerfStats::inUseAttachmentRigged);
}
else
{
--FSPerfStats::inUseAttachmentUnRigged;
LL_PROFILE_PLOT("InUseAttachmentUnRigged", (int64_t)FSPerfStats::inUseAttachmentUnRigged);
--LLPerfStats::inUseAttachmentUnRigged;
LL_PROFILE_PLOT("InUseAttachmentUnRigged", (int64_t)LLPerfStats::inUseAttachmentUnRigged);
}
}
if (stat.objType == FSPerfStats::ObjType_t::OT_GENERAL)
if (stat.objType == LLPerfStats::ObjType_t::OT_GENERAL)
{
--FSPerfStats::inUseScene;
LL_PROFILE_PLOT("InUseScene", (int64_t)FSPerfStats::inUseScene);
--LLPerfStats::inUseScene;
LL_PROFILE_PLOT("InUseScene", (int64_t)LLPerfStats::inUseScene);
}
if( stat.objType == FSPerfStats::ObjType_t::OT_AVATAR )
if( stat.objType == LLPerfStats::ObjType_t::OT_AVATAR )
{
--FSPerfStats::inUseAvatar;
LL_PROFILE_PLOT("InUseAv", (int64_t)FSPerfStats::inUseAvatar);
--LLPerfStats::inUseAvatar;
LL_PROFILE_PLOT("InUseAv", (int64_t)LLPerfStats::inUseAvatar);
}
#endif
// </FS:Beq>
@ -501,13 +443,19 @@ namespace LLPerfStats
StatsRecorder::send(std::move(stat));
};
};
inline double raw_to_ns(U64 raw) { return (static_cast<double>(raw) * 1000000000.0) / LLPerfStats::cpu_hertz; };
inline double raw_to_us(U64 raw) { return (static_cast<double>(raw) * 1000000.0) / LLPerfStats::cpu_hertz; };
inline double raw_to_ms(U64 raw) { return (static_cast<double>(raw) * 1000.0) / LLPerfStats::cpu_hertz; };
inline U64 ns_to_raw(double ns) { return (U64)(LLPerfStats::cpu_hertz * (ns / 1000000000.0)); }
inline U64 us_to_raw(double us) { return (U64)(LLPerfStats::cpu_hertz * (us / 1000000.0)); }
inline U64 ms_to_raw(double ms) { return (U64)(LLPerfStats::cpu_hertz * (ms / 1000.0));
}
using RecordSceneTime = RecordTime<ObjType_t::OT_GENERAL>;
using RecordAvatarTime = RecordTime<ObjType_t::OT_AVATAR>;
};// namespace LLPerfStats

View File

@ -478,7 +478,7 @@ void update_statistics()
auto tot_frame_time_raw = LLPerfStats::StatsRecorder::getSceneStat(LLPerfStats::StatType_t::RENDER_FRAME);
// cumulative avatar time (includes idle processing, attachments and base av)
auto tot_avatar_time_raw = LLPerfStats::StatsRecorder::getSum(LLPerfStats::ObjType_t::OT_AVATAR, LLPerfStats::StatType_t::RENDER_COMBINED);
auto tot_avatar_time_raw = LLPerfStats::us_to_raw(LLVOAvatar::getTotalGPURenderTime());
// cumulative avatar render specific time (a bit arbitrary as the processing is too.)
// auto tot_av_idle_time_raw = LLPerfStats::StatsRecorder::getSum(AvType, LLPerfStats::StatType_t::RENDER_IDLE);
// auto tot_avatar_render_time_raw = tot_avatar_time_raw - tot_av_idle_time_raw;

View File

@ -2716,7 +2716,6 @@ void LLVOAvatar::idleUpdate(LLAgent &agent, const F64 &time)
return;
}
// record time and refresh "tooSlow" status
LLPerfStats::RecordAvatarTime T(getID(), LLPerfStats::StatType_t::RENDER_IDLE); // per avatar "idle" time.
updateTooSlow();
static LLCachedControl<bool> disable_all_render_types(gSavedSettings, "DisableAllRenderTypes");
@ -9242,31 +9241,6 @@ bool LLVOAvatar::isTooSlow() const
return mTooSlow;
}
// use Avatar Render Time as complexity metric
// <FS:Beq> refactor for clarity post LL merge
void LLVOAvatar::clearSlowARTCache()
{
mARTStale = false;
mTooSlow = false;
mTooSlowWithoutShadows = false;
}
void LLVOAvatar::setSlowARTCache(U64 full_render_time, U64 non_shadow_render_time)
{
mLastARTUpdateFrame = LLFrameTimer::getFrameCount();
mRenderTime = full_render_time;
mRenderTimeNoShadows = non_shadow_render_time;
mARTStale = false;
mTooSlow = true;
}
// </FS:Beq>
// markARTStale - Mark stale and set the frameupdate to now so that we can wait at least one frame to get a revised number.
void LLVOAvatar::markARTStale()
{
mARTStale=true;
mLastARTUpdateFrame = LLFrameTimer::getFrameCount();
}
// Udpate Avatar state based on render time
void LLVOAvatar::updateTooSlow()
{
@ -9278,50 +9252,9 @@ void LLVOAvatar::updateTooSlow()
// mTooSlow - Is the avatar flagged as being slow (includes shadow time)
// mTooSlowWithoutShadows - Is the avatar flagged as being slow even with shadows removed.
// mARTStale - the rendertime we have is stale because of an update. We need to force a re-render to re-assess slowness
if( mARTStale )
{
if ( LLFrameTimer::getFrameCount() - mLastARTUpdateFrame < 5 )
{
// LL_INFOS() << this->getFullname() << " marked stale " << LL_ENDL;
// we've not had a chance to update yet (allow a few to be certain a full frame has passed)
return;
}
// <FS:Beq> refactor and work out why shadow derendering is no longer working
// mARTStale = false;
// mTooSlow = false;
// mTooSlowWithoutShadows = false;
// LL_INFOS() << this->getFullname() << " refreshed ART combined = " << mRenderTime << " @ " << mLastARTUpdateFrame << LL_ENDL;
clearSlowARTCache();
changed_slow_state = true;
// </FS:Beq>
}
// Either we're not stale or we've updated.
U64 render_time_raw;
U64 render_time_no_shadows_raw; // <FS:Beq/> rename as we now include idle time
if( !mTooSlow )
{
// we are fully rendered, so we use the live values
std::lock_guard<std::mutex> lock{LLPerfStats::bufferToggleLock};
render_time_raw = LLPerfStats::StatsRecorder::get(LLPerfStats::ObjType_t::OT_AVATAR, id, LLPerfStats::StatType_t::RENDER_COMBINED);
// <FS:Beq> include idle time in total render time
// render_geom_time_raw = LLPerfStats::StatsRecorder::get(LLPerfStats::ObjType_t::OT_AVATAR, id, LLPerfStats::StatType_t::RENDER_GEOMETRY);
render_time_no_shadows_raw = render_time_raw - LLPerfStats::StatsRecorder::get(LLPerfStats::ObjType_t::OT_AVATAR, id, LLPerfStats::StatType_t::RENDER_SHADOWS);
// </FS:Beq>
}
else
{
// use the cached values.
render_time_raw = mRenderTime;
// <FS:Beq> variable name updated to refelect different meaning.
//render_geom_time_raw = mGeomTime;
render_time_no_shadows_raw = mRenderTimeNoShadows;
// <FS:Beq>
}
// get max render time in ms
F32 max_art_ms = (F32) (LLPerfStats::renderAvatarMaxART_ns / 1000000.0);
bool autotune = LLPerfStats::tunables.userAutoTuneEnabled && !mIsControlAvatar && !isSelf();
@ -9337,23 +9270,13 @@ void LLVOAvatar::updateTooSlow()
}
bool exceeds_max_ART =
((LLPerfStats::renderAvatarMaxART_ns > 0) && (LLPerfStats::raw_to_ns(render_time_raw) >= LLPerfStats::renderAvatarMaxART_ns));
((LLPerfStats::renderAvatarMaxART_ns > 0) &&
(mGPURenderTime >= max_art_ms)); // NOTE: don't use getGPURenderTime accessor here to avoid "isTooSlow" feedback loop
if (exceeds_max_ART && !ignore_tune)
{
if( !mTooSlow ) // if we were previously not slow (with or without shadows.)
{
// if we weren't capped, we are now
// <FS:Beq> refactored "geom" becomes "no shadow"
// mLastARTUpdateFrame = LLFrameTimer::getFrameCount();
// mRenderTime = render_time_raw;
// mGeomTime = render_geom_time_raw;
// mARTStale = false;
// mTooSlow = true;
setSlowARTCache(render_time_raw, render_time_no_shadows_raw);
changed_slow_state = true;
// </FS:Beq>
}
mTooSlow = true;
if(!mTooSlowWithoutShadows) // if we were not previously above the full impostor cap
{
bool render_friend_or_exception = ( alwaysRenderFriends && LLAvatarTracker::instance().isBuddy( id ) ) ||
@ -9361,9 +9284,7 @@ void LLVOAvatar::updateTooSlow()
if( (!isSelf() || allowSelfImpostor) && !render_friend_or_exception )
{
// Note: slow rendering Friends still get their shadows zapped.
// <FS:Beq> changes to support idel and geom in non shadow rendering cost + improved dirty marking
// mTooSlowWithoutShadows = (LLPerfStats::raw_to_ns(render_geom_time_raw) >= LLPerfStats::renderAvatarMaxART_ns);
mTooSlowWithoutShadows = (LLPerfStats::raw_to_ns(render_time_no_shadows_raw) >= LLPerfStats::renderAvatarMaxART_ns);
mTooSlowWithoutShadows = getGPURenderTime()*2.f >= max_art_ms; // NOTE: assumes shadow rendering doubles render time
}
if(mTooSlowWithoutShadows)
{
@ -9376,8 +9297,6 @@ void LLVOAvatar::updateTooSlow()
else
{
// <FS:Beq> better state change flagging
// LL_INFOS() << this->getFullname() << " ("<< (combined?"combined":"geometry") << ") good render time = " << LLPerfStats::raw_to_ns(render_time_raw) << " vs ("<< LLVOAvatar::sRenderTimeCap_ns << " set @ " << mLastARTUpdateFrame << LL_ENDL;
// LL_INFOS() << this->getFullname() << " good render time = " << render_time_ns << " vs ("<< LLPerfStats::renderAvatarMaxART_ns << " set @ " << mLastARTUpdateFrame << ")" << LL_ENDL;
if( mTooSlow || mTooSlowWithoutShadows )
{
changed_slow_state = true;
@ -11965,7 +11884,6 @@ void LLVOAvatar::updateVisualComplexity()
LL_DEBUGS("AvatarRender") << "avatar " << getID() << " appearance changed" << LL_ENDL;
// Set the cache time to in the past so it's updated ASAP
mVisualComplexityStale = true;
markARTStale();
}
@ -12287,6 +12205,22 @@ void LLVOAvatar::calculateUpdateRenderComplexity()
LLSidepanelAppearance::updateAvatarComplexity(mVisualComplexity, item_complexity, temp_item_complexity, body_parts_complexity);
}
// </FS:Ansariel>
//schedule an update to ART next frame if needed
if (LLPerfStats::tunables.userAutoTuneEnabled &&
LLPerfStats::tunables.userFPSTuningStrategy != LLPerfStats::TUNE_SCENE_ONLY &&
!isVisuallyMuted())
{
LLUUID id = getID(); // <== use id to make sure this avatar didn't get deleted between frames
LL::WorkQueue::getInstance("mainloop")->post([this, id]()
{
if (gObjectList.findObject(id) != nullptr)
{
gPipeline.profileAvatar(this);
}
});
}
}
}
@ -12295,7 +12229,6 @@ void LLVOAvatar::setVisualMuteSettings(VisualMuteSettings set)
mVisuallyMuteSetting = set;
mNeedsImpostorUpdate = TRUE;
mLastImpostorUpdateReason = 7;
markARTStale();// <FS:Beq> Force a refresh of the ART to take into account new setting.
// <FS:Ansariel> [FS Persisted Avatar Render Settings]
//LLRenderMuteList::getInstance()->saveVisualMuteSetting(getID(), S32(set));
@ -12678,6 +12611,9 @@ void LLVOAvatar::readProfileQuery(S32 retries)
glGetQueryObjectui64v(mGPUTimerQuery, GL_QUERY_RESULT, &time_elapsed);
mGPURenderTime = time_elapsed / 1000000.f;
mGPUProfilePending = false;
setDebugText(llformat("%d", (S32)(mGPURenderTime * 1000.f)));
}
else
{ // wait until next frame
@ -12690,6 +12626,70 @@ void LLVOAvatar::readProfileQuery(S32 retries)
}
}
F32 LLVOAvatar::getGPURenderTime()
{
return isVisuallyMuted() ? 0.f : mGPURenderTime;
}
// static
F32 LLVOAvatar::getTotalGPURenderTime()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;
F32 ret = 0.f;
for (LLCharacter* iter : LLCharacter::sInstances)
{
LLVOAvatar* inst = (LLVOAvatar*) iter;
ret += inst->getGPURenderTime();
}
return ret;
}
F32 LLVOAvatar::getMaxGPURenderTime()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;
F32 ret = 0.f;
for (LLCharacter* iter : LLCharacter::sInstances)
{
LLVOAvatar* inst = (LLVOAvatar*)iter;
ret = llmax(inst->getGPURenderTime(), ret);
}
return ret;
}
F32 LLVOAvatar::getAverageGPURenderTime()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;
F32 ret = 0.f;
S32 count = 0;
for (LLCharacter* iter : LLCharacter::sInstances)
{
LLVOAvatar* inst = (LLVOAvatar*)iter;
if (!inst->isTooSlow())
{
ret += inst->getGPURenderTime();
++count;
}
}
if (count > 0)
{
ret /= count;
}
return ret;
}
// <FS:Ansariel> [Legacy Bake]
//-----------------------------------------------------------------------------
// Legacy baking

View File

@ -324,11 +324,21 @@ public:
void readProfileQuery(S32 retries);
// get the GPU time in ms of rendering this avatar including all attachments
// returns -1 if this avatar has not been profiled using gPipeline.profileAvatar
F32 getGPURenderTime() { return mGPURenderTime; }
// returns 0.f if this avatar has not been profiled using gPipeline.profileAvatar
// or the avatar is visually muted
F32 getGPURenderTime();
// get the total GPU render time in ms of all avatars that have been benched
static F32 getTotalGPURenderTime();
// get the max GPU render time in ms of all avatars that have been benched
static F32 getMaxGPURenderTime();
// get the average GPU render time in ms of all avatars that have been benched
static F32 getAverageGPURenderTime();
// get the CPU time in ms of rendering this avatar including all attachments
// return -1 if this avatar has not been profiled using gPipeline.mProfileAvatar
// return 0.f if this avatar has not been profiled using gPipeline.mProfileAvatar
F32 getCPURenderTime() { return mCPURenderTime; }
@ -419,12 +429,7 @@ public:
void logMetricsTimerRecord(const std::string& phase_name, F32 elapsed, bool completed);
void calcMutedAVColor();
void markARTStale();
// <FS:Beq> refactoring post LL merge
void clearSlowARTCache();
void setSlowARTCache(U64 full_render_time, U64 geometry_render_time);
// </FS:Beq>
protected:
LLViewerStats::PhaseMap& getPhases() { return mPhases; }
BOOL updateIsFullyLoaded();
@ -445,14 +450,6 @@ private:
LLFrameTimer mFullyLoadedTimer;
LLFrameTimer mRuthTimer;
U32 mLastARTUpdateFrame{0};
U64 mRenderTime{0};
// <FS:Beq> variable name updated to refelect different meaning.
//U64 mGeomTime{ 0 };
U64 mRenderTimeNoShadows{0};
// </FS:Beq>
bool mARTStale{true};
bool mARTCapped{false};
// variables to hold "slowness" status
bool mTooSlow{false};
bool mTooSlowWithoutShadows{false};
@ -584,11 +581,11 @@ private:
// profile results
// GPU render time in ms
F32 mGPURenderTime = -1.f;
F32 mGPURenderTime = 0.f;
bool mGPUProfilePending = false;
// CPU render time in ms
F32 mCPURenderTime = -1.f;
F32 mCPURenderTime = 0.f;
// the isTooComplex method uses these mutable values to avoid recalculating too frequently
// DEPRECATED -- obsolete avatar render cost values
@ -1276,8 +1273,6 @@ public:
// COF version of last appearance message received for this av.
S32 mLastUpdateReceivedCOFVersion;
U64 getLastART() const { return mRenderTime; }
/** Diagnostics
** **
*******************************************************************************/

View File

@ -1956,7 +1956,11 @@ F32 LLWorld::getNearbyAvatarsAndMaxGPUTime(std::vector<LLCharacter*> &valid_near
char_iter++;
continue;
}
gPipeline.profileAvatar(avatar);
if (!avatar->isTooSlow())
{
gPipeline.profileAvatar(avatar);
}
nearby_max_complexity = llmax(nearby_max_complexity, avatar->getGPURenderTime());
valid_nearby_avs.push_back(*char_iter);
}

View File

@ -2353,12 +2353,18 @@ bool LLPipeline::getVisibleExtents(LLCamera& camera, LLVector3& min, LLVector3&
static LLTrace::BlockTimerStatHandle FTM_CULL("Object Culling");
// static
bool LLPipeline::isWaterClip()
{
return (!sRenderTransparentWater || gCubeSnapshot) && !sRenderingHUDs;
}
void LLPipeline::updateCull(LLCamera& camera, LLCullResult& result, bool hud_attachments)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_PIPELINE; //LL_RECORD_BLOCK_TIME(FTM_CULL);
LL_PROFILE_GPU_ZONE("updateCull"); // should always be zero GPU time, but drop a timer to flush stuff out
bool water_clip = !sRenderTransparentWater && !sRenderingHUDs;
bool water_clip = isWaterClip();
if (water_clip)
{
@ -10235,6 +10241,9 @@ void LLPipeline::profileAvatar(LLVOAvatar* avatar, bool profile_attachments)
LL_PROFILE_ZONE_SCOPED_CATEGORY_PIPELINE;
// don't continue to profile an avatar that is known to be too slow
llassert(!avatar->isTooSlow());
LLGLSLShader* cur_shader = LLGLSLShader::sCurBoundShaderPtr;
mRT->deferredScreen.bindTarget();
@ -10513,25 +10522,28 @@ void LLPipeline::generateImpostor(LLVOAvatar* avatar, bool preview_avatar, bool
resY = llmin(nhpo2((U32) (fov*pa)), (U32) 512);
resX = llmin(nhpo2((U32) (atanf(tdim.mV[0]/distance)*2.f*RAD_TO_DEG*pa)), (U32) 512);
if (!avatar->mImpostor.isComplete())
{
avatar->mImpostor.allocate(resX, resY, GL_RGBA, true);
if (!for_profile)
{
if (!avatar->mImpostor.isComplete())
{
avatar->mImpostor.allocate(resX, resY, GL_RGBA, true);
if (LLPipeline::sRenderDeferred)
{
addDeferredAttachments(avatar->mImpostor, true);
}
gGL.getTexUnit(0)->bind(&avatar->mImpostor);
gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT);
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
}
else if(resX != avatar->mImpostor.getWidth() || resY != avatar->mImpostor.getHeight())
{
avatar->mImpostor.resize(resX,resY);
}
if (LLPipeline::sRenderDeferred)
{
addDeferredAttachments(avatar->mImpostor, true);
}
avatar->mImpostor.bindTarget();
gGL.getTexUnit(0)->bind(&avatar->mImpostor);
gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT);
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
}
else if (resX != avatar->mImpostor.getWidth() || resY != avatar->mImpostor.getHeight())
{
avatar->mImpostor.resize(resX, resY);
}
avatar->mImpostor.bindTarget();
}
}
F32 old_alpha = LLDrawPoolAvatar::sMinimumAlpha;
@ -10647,7 +10659,7 @@ void LLPipeline::generateImpostor(LLVOAvatar* avatar, bool preview_avatar, bool
gGL.popMatrix();
}
if (!preview_avatar)
if (!preview_avatar && !for_profile)
{
avatar->mImpostor.flush();
avatar->setImpostorDim(tdim);

View File

@ -376,6 +376,8 @@ public:
bool hasRenderType(const U32 type) const;
bool hasAnyRenderType(const U32 type, ...) const;
static bool isWaterClip();
void setRenderTypeMask(U32 type, ...);
// This is equivalent to 'setRenderTypeMask'
//void orRenderTypeMask(U32 type, ...);