From 8ef25a63bd1e84e02fe2c08ce439aa26c0f3d5f1 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 23 May 2022 19:49:18 -0700 Subject: [PATCH 01/13] SL-15937 - adjust memory limit / cache code --- indra/llcommon/llmemory.cpp | 8 ++-- indra/newview/app_settings/settings.xml | 6 +-- indra/newview/llvocache.cpp | 50 ++++++++++++------------- 3 files changed, 31 insertions(+), 33 deletions(-) diff --git a/indra/llcommon/llmemory.cpp b/indra/llcommon/llmemory.cpp index ea84e4c1ea..afd476096e 100644 --- a/indra/llcommon/llmemory.cpp +++ b/indra/llcommon/llmemory.cpp @@ -210,11 +210,9 @@ U64 LLMemory::getCurrentRSS() mach_msg_type_number_t basicInfoCount = MACH_TASK_BASIC_INFO_COUNT; if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&basicInfo, &basicInfoCount) == KERN_SUCCESS) { -// residentSize = basicInfo.resident_size; - // Although this method is defined to return the "resident set size," - // in fact what callers want from it is the total virtual memory - // consumed by the application. - residentSize = basicInfo.virtual_size; + residentSize = basicInfo.resident_size; + // 64-bit macos apps allocate 32 GB or more at startup, and this is reflected in virtual_size. + // basicInfo.virtual_size is not what we want. } else { diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index c13329dc4a..463daeaa76 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -7077,13 +7077,13 @@ NonvisibleObjectsInMemoryTime Comment - Number of frames non-visible objects stay in memory before being removed. 0 means never to remove. + Number of frames non-visible objects stay in memory before being removed. 0 means max. Persist 1 Type U32 Value - 300 + 64 NoPreload @@ -11011,7 +11011,7 @@ Type U32 Value - 1024 + 2048 SceneLoadLowMemoryBound diff --git a/indra/newview/llvocache.cpp b/indra/newview/llvocache.cpp index 5ebc65405f..1579eb304e 100644 --- a/indra/newview/llvocache.cpp +++ b/indra/newview/llvocache.cpp @@ -369,15 +369,6 @@ void LLVOCacheEntry::updateDebugSettings() } timer.reset(); - //the number of frames invisible objects stay in memory - static LLCachedControl inv_obj_time(gSavedSettings,"NonvisibleObjectsInMemoryTime"); - sMinFrameRange = inv_obj_time - 1; //make 0 to be the maximum - - //min radius: all objects within this radius remain loaded in memory - static LLCachedControl min_radius(gSavedSettings,"SceneLoadMinRadius"); - sNearRadius = llmin((F32)min_radius, gAgentCamera.mDrawDistance); //can not exceed the draw distance - sNearRadius = llmax(sNearRadius, 1.f); //minimum value is 1.0m - //objects within the view frustum whose visible area is greater than this threshold will be loaded static LLCachedControl front_pixel_threshold(gSavedSettings,"SceneLoadFrontPixelThreshold"); sFrontPixelThreshold = front_pixel_threshold; @@ -387,29 +378,37 @@ void LLVOCacheEntry::updateDebugSettings() sRearPixelThreshold = rear_pixel_threshold; sRearPixelThreshold = llmax(sRearPixelThreshold, sFrontPixelThreshold); //can not be smaller than sFrontPixelThreshold. - // a percentage of draw distance beyond which all objects outside of view frustum will be unloaded, regardless of pixel threshold - static LLCachedControl rear_max_radius_frac(gSavedSettings,"SceneLoadRearMaxRadiusFraction"); - sRearFarRadius = llmax(rear_max_radius_frac * gAgentCamera.mDrawDistance / 100.f, 1.0f); //minimum value is 1.0m - sRearFarRadius = llmax(sRearFarRadius, (F32)min_radius); //can not be less than "SceneLoadMinRadius". - sRearFarRadius = llmin(sRearFarRadius, gAgentCamera.mDrawDistance); //can not be more than the draw distance. - - //make the above parameters adaptive to memory usage + //make parameters adaptive to memory usage //starts to put restrictions from low_mem_bound_MB, apply tightest restrictions when hits high_mem_bound_MB static LLCachedControl low_mem_bound_MB(gSavedSettings,"SceneLoadLowMemoryBound"); static LLCachedControl high_mem_bound_MB(gSavedSettings,"SceneLoadHighMemoryBound"); LLMemory::updateMemoryInfo() ; U32 allocated_mem = LLMemory::getAllocatedMemKB().value(); - allocated_mem /= 1024; //convert to MB. - if(allocated_mem < low_mem_bound_MB) - { - return; - } - F32 adjust_factor = llmax(0.f, (F32)(high_mem_bound_MB - allocated_mem) / (high_mem_bound_MB - low_mem_bound_MB)); + static const F32 KB_to_MB = 1.f / 1024.f; + U32 clamped_memory = llclamp(allocated_mem * KB_to_MB, (F32) low_mem_bound_MB, (F32) high_mem_bound_MB); + const F32 adjust_range = high_mem_bound_MB - low_mem_bound_MB; + const F32 adjust_factor = (high_mem_bound_MB - clamped_memory) / adjust_range; // [0, 1] - sRearFarRadius = llmin(adjust_factor * sRearFarRadius, 96.f); //[0.f, 96.f] - sMinFrameRange = (U32)llclamp(adjust_factor * sMinFrameRange, 10.f, 64.f); //[10, 64] - sNearRadius = llmax(adjust_factor * sNearRadius, 1.0f); + //min radius: all objects within this radius remain loaded in memory + static LLCachedControl min_radius(gSavedSettings,"SceneLoadMinRadius"); + static const F32 MIN_RADIUS = 1.0f; + const F32 draw_radius = gAgentCamera.mDrawDistance; + const F32 clamped_min_radius = llclamp((F32) min_radius, MIN_RADIUS, draw_radius); // [1, mDrawDistance] + sNearRadius = MIN_RADIUS + ((clamped_min_radius - MIN_RADIUS) * adjust_factor); + + // a percentage of draw distance beyond which all objects outside of view frustum will be unloaded, regardless of pixel threshold + static LLCachedControl rear_max_radius_frac(gSavedSettings,"SceneLoadRearMaxRadiusFraction"); + const F32 max_radius = rear_max_radius_frac * gAgentCamera.mDrawDistance; + const F32 clamped_max_radius = llclamp(max_radius, sNearRadius, draw_radius); // [sNearRadius, mDrawDistance] + sRearFarRadius = sNearRadius + ((clamped_max_radius - sNearRadius) * adjust_factor); + + //the number of frames invisible objects stay in memory + static LLCachedControl inv_obj_time(gSavedSettings,"NonvisibleObjectsInMemoryTime"); + static const U32 MIN_FRAMES = 10; + static const U32 MAX_FRAMES = 64; + const U32 clamped_frames = inv_obj_time ? llclamp((U32) inv_obj_time, MIN_FRAMES, MAX_FRAMES) : MAX_FRAMES; // [10, 64], with zero => 64 + sMinFrameRange = MIN_FRAMES + ((clamped_frames - MIN_FRAMES) * adjust_factor); } //static @@ -504,6 +503,7 @@ void LLVOCacheEntry::calcSceneContribution(const LLVector4a& camera_origin, bool { mSceneContrib = 0.f; //out of draw distance, not to load } + } setVisible(); From 10f56db475316433586fee273cd442b143a31c08 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 24 May 2022 07:04:54 -0700 Subject: [PATCH 02/13] SL-15937 - far must be greater than near near, not equal --- indra/newview/llvocache.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indra/newview/llvocache.cpp b/indra/newview/llvocache.cpp index 1579eb304e..89dab23399 100644 --- a/indra/newview/llvocache.cpp +++ b/indra/newview/llvocache.cpp @@ -399,9 +399,10 @@ void LLVOCacheEntry::updateDebugSettings() // a percentage of draw distance beyond which all objects outside of view frustum will be unloaded, regardless of pixel threshold static LLCachedControl rear_max_radius_frac(gSavedSettings,"SceneLoadRearMaxRadiusFraction"); + const F32 min_radius_plus_one = sNearRadius; const F32 max_radius = rear_max_radius_frac * gAgentCamera.mDrawDistance; - const F32 clamped_max_radius = llclamp(max_radius, sNearRadius, draw_radius); // [sNearRadius, mDrawDistance] - sRearFarRadius = sNearRadius + ((clamped_max_radius - sNearRadius) * adjust_factor); + const F32 clamped_max_radius = llclamp(max_radius, min_radius_plus_one, draw_radius); // [sNearRadius, mDrawDistance] + sRearFarRadius = min_radius_plus_one + ((clamped_max_radius - min_radius_plus_one) * adjust_factor); //the number of frames invisible objects stay in memory static LLCachedControl inv_obj_time(gSavedSettings,"NonvisibleObjectsInMemoryTime"); @@ -503,7 +504,6 @@ void LLVOCacheEntry::calcSceneContribution(const LLVector4a& camera_origin, bool { mSceneContrib = 0.f; //out of draw distance, not to load } - } setVisible(); From f55c1ff40562a6f46a5508c7308d335a9aadb68f Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 24 May 2022 09:33:06 -0700 Subject: [PATCH 03/13] SL-15937 - forget the +1 --- indra/newview/llvocache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/llvocache.cpp b/indra/newview/llvocache.cpp index 89dab23399..3e5f1bdc7f 100644 --- a/indra/newview/llvocache.cpp +++ b/indra/newview/llvocache.cpp @@ -399,7 +399,7 @@ void LLVOCacheEntry::updateDebugSettings() // a percentage of draw distance beyond which all objects outside of view frustum will be unloaded, regardless of pixel threshold static LLCachedControl rear_max_radius_frac(gSavedSettings,"SceneLoadRearMaxRadiusFraction"); - const F32 min_radius_plus_one = sNearRadius; + const F32 min_radius_plus_one = sNearRadius + 1.f; const F32 max_radius = rear_max_radius_frac * gAgentCamera.mDrawDistance; const F32 clamped_max_radius = llclamp(max_radius, min_radius_plus_one, draw_radius); // [sNearRadius, mDrawDistance] sRearFarRadius = min_radius_plus_one + ((clamped_max_radius - min_radius_plus_one) * adjust_factor); From 4cfa59d3f1b856f62ab18543b1dbefbc574fb218 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 26 May 2022 21:25:23 +0300 Subject: [PATCH 04/13] SL-17473 Viewer not clearing all Vertex Buffers in some cases Image thread doesn't need mBuffer and buffer isn't thread safe so no point allocating it in an image thread. --- indra/llrender/llimagegl.cpp | 2 +- indra/llrender/llrender.cpp | 77 ++++++++++++++++++++----------- indra/llrender/llrender.h | 4 +- indra/llrender/llvertexbuffer.cpp | 2 +- indra/llrender/llvertexbuffer.h | 1 - indra/newview/llviewerwindow.cpp | 2 +- indra/newview/llvoavatar.cpp | 3 +- indra/newview/pipeline.cpp | 8 +++- 8 files changed, 64 insertions(+), 35 deletions(-) diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 9bd3a0a6b0..46f0095c92 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -2432,7 +2432,7 @@ void LLImageGLThread::run() // We must perform setup on this thread before actually servicing our // WorkQueue, likewise cleanup afterwards. mWindow->makeContextCurrent(mContext); - gGL.init(); + gGL.init(false); ThreadPool::run(); gGL.shutdown(); mWindow->destroySharedContext(mContext); diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp index a03a27cf94..72cca1f2a2 100644 --- a/indra/llrender/llrender.cpp +++ b/indra/llrender/llrender.cpp @@ -869,7 +869,7 @@ LLRender::~LLRender() shutdown(); } -void LLRender::init() +void LLRender::init(bool needs_vertex_buffer) { #if LL_WINDOWS if (gGLManager.mHasDebugOutput && gDebugGL) @@ -897,15 +897,27 @@ void LLRender::init() #endif } + if (needs_vertex_buffer) + { + initVertexBuffer(); + } +} - llassert_always(mBuffer.isNull()) ; - stop_glerror(); - mBuffer = new LLVertexBuffer(immediate_mask, 0); - mBuffer->allocateBuffer(4096, 0, TRUE); - mBuffer->getVertexStrider(mVerticesp); - mBuffer->getTexCoord0Strider(mTexcoordsp); - mBuffer->getColorStrider(mColorsp); - stop_glerror(); +void LLRender::initVertexBuffer() +{ + llassert_always(mBuffer.isNull()); + stop_glerror(); + mBuffer = new LLVertexBuffer(immediate_mask, 0); + mBuffer->allocateBuffer(4096, 0, TRUE); + mBuffer->getVertexStrider(mVerticesp); + mBuffer->getTexCoord0Strider(mTexcoordsp); + mBuffer->getColorStrider(mColorsp); + stop_glerror(); +} + +void LLRender::resetVertexBuffer() +{ + mBuffer = NULL; } void LLRender::shutdown() @@ -923,7 +935,7 @@ void LLRender::shutdown() delete mLightState[i]; } mLightState.clear(); - mBuffer = NULL ; + resetVertexBuffer(); } void LLRender::refreshState(void) @@ -1625,25 +1637,34 @@ void LLRender::flush() mCount = 0; - if (mBuffer->useVBOs() && !mBuffer->isLocked()) - { //hack to only flush the part of the buffer that was updated (relies on stream draw using buffersubdata) - mBuffer->getVertexStrider(mVerticesp, 0, count); - mBuffer->getTexCoord0Strider(mTexcoordsp, 0, count); - mBuffer->getColorStrider(mColorsp, 0, count); - } - - mBuffer->flush(); - mBuffer->setBuffer(immediate_mask); + if (mBuffer) + { + if (mBuffer->useVBOs() && !mBuffer->isLocked()) + { //hack to only flush the part of the buffer that was updated (relies on stream draw using buffersubdata) + mBuffer->getVertexStrider(mVerticesp, 0, count); + mBuffer->getTexCoord0Strider(mTexcoordsp, 0, count); + mBuffer->getColorStrider(mColorsp, 0, count); + } + + mBuffer->flush(); + mBuffer->setBuffer(immediate_mask); + + if (mMode == LLRender::QUADS && sGLCoreProfile) + { + mBuffer->drawArrays(LLRender::TRIANGLES, 0, count); + mQuadCycle = 1; + } + else + { + mBuffer->drawArrays(mMode, 0, count); + } + } + else + { + // mBuffer is present in main thread and not present in an image thread + LL_ERRS() << "A flush call from outside main rendering thread" << LL_ENDL; + } - if (mMode == LLRender::QUADS && sGLCoreProfile) - { - mBuffer->drawArrays(LLRender::TRIANGLES, 0, count); - mQuadCycle = 1; - } - else - { - mBuffer->drawArrays(mMode, 0, count); - } mVerticesp[0] = mVerticesp[count]; mTexcoordsp[0] = mTexcoordsp[count]; diff --git a/indra/llrender/llrender.h b/indra/llrender/llrender.h index e2489876e4..9c36c230fb 100644 --- a/indra/llrender/llrender.h +++ b/indra/llrender/llrender.h @@ -354,7 +354,9 @@ public: LLRender(); ~LLRender(); - void init() ; + void init(bool needs_vertex_buffer); + void initVertexBuffer(); + void resetVertexBuffer(); void shutdown(); // Refreshes renderer state to the cached values diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index 6338cab96a..be3e6ddff0 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -1055,7 +1055,7 @@ void LLVertexBuffer::releaseIndices() bool LLVertexBuffer::createGLBuffer(U32 size) { - if (mGLBuffer) + if (mGLBuffer || mMappedData) { destroyGLBuffer(); } diff --git a/indra/llrender/llvertexbuffer.h b/indra/llrender/llvertexbuffer.h index baf8407fc6..3b3fe44984 100644 --- a/indra/llrender/llvertexbuffer.h +++ b/indra/llrender/llvertexbuffer.h @@ -265,7 +265,6 @@ public: bool getTangentStrider(LLStrider& strider, S32 index=0, S32 count = -1, bool map_range = false); bool getTangentStrider(LLStrider& strider, S32 index=0, S32 count = -1, bool map_range = false); bool getColorStrider(LLStrider& strider, S32 index=0, S32 count = -1, bool map_range = false); - bool getTextureIndexStrider(LLStrider& strider, S32 index=0, S32 count = -1, bool map_range = false); bool getEmissiveStrider(LLStrider& strider, S32 index=0, S32 count = -1, bool map_range = false); bool getWeightStrider(LLStrider& strider, S32 index=0, S32 count = -1, bool map_range = false); bool getWeight4Strider(LLStrider& strider, S32 index=0, S32 count = -1, bool map_range = false); diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 8e565bbdca..33e8178593 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -1998,7 +1998,7 @@ LLViewerWindow::LLViewerWindow(const Params& p) } LLVertexBuffer::initClass(gSavedSettings.getBOOL("RenderVBOEnable"), gSavedSettings.getBOOL("RenderVBOMappingDisable")); LL_INFOS("RenderInit") << "LLVertexBuffer initialization done." << LL_ENDL ; - gGL.init() ; + gGL.init(true); if (LLFeatureManager::getInstance()->isSafe() || (gSavedSettings.getS32("LastFeatureVersion") != LLFeatureManager::getInstance()->getVersion()) diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 37851ce99b..5dfcb68562 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -2810,6 +2810,7 @@ void LLVOAvatar::idleUpdateMisc(bool detailed_update) if (detailed_update) { U32 draw_order = 0; + S32 attachment_selected = LLSelectMgr::getInstance()->getSelection()->getObjectCount() && LLSelectMgr::getInstance()->getSelection()->isAttachment(); for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); iter != mAttachmentPoints.end(); ++iter) @@ -2849,7 +2850,7 @@ void LLVOAvatar::idleUpdateMisc(bool detailed_update) } // if selecting any attachments, update all of them as non-damped - if (LLSelectMgr::getInstance()->getSelection()->getObjectCount() && LLSelectMgr::getInstance()->getSelection()->isAttachment()) + if (attachment_selected) { gPipeline.updateMoveNormalAsync(attached_object->mDrawable); } diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index da16c8209f..db5c446e7a 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -7238,6 +7238,7 @@ void LLPipeline::doResetVertexBuffers(bool forced) mResetVertexBuffers = false; mCubeVB = NULL; + mDeferredVB = NULL; for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); iter != LLWorld::getInstance()->getRegionList().end(); ++iter) @@ -7271,10 +7272,11 @@ void LLPipeline::doResetVertexBuffers(bool forced) LLPathingLib::getInstance()->cleanupVBOManager(); } LLVOPartGroup::destroyGL(); + gGL.resetVertexBuffer(); SUBSYSTEM_CLEANUP(LLVertexBuffer); - if (LLVertexBuffer::sGLCount > 0) + if (LLVertexBuffer::sGLCount != 0) { LL_WARNS() << "VBO wipe failed -- " << LLVertexBuffer::sGLCount << " buffers remaining." << LL_ENDL; } @@ -7295,6 +7297,10 @@ void LLPipeline::doResetVertexBuffers(bool forced) LLPipeline::sTextureBindTest = gSavedSettings.getBOOL("RenderDebugTextureBind"); LLVertexBuffer::initClass(LLVertexBuffer::sEnableVBOs, LLVertexBuffer::sDisableVBOMapping); + gGL.initVertexBuffer(); + + mDeferredVB = new LLVertexBuffer(DEFERRED_VB_MASK, 0); + mDeferredVB->allocateBuffer(8, 0, true); LLVOPartGroup::restoreGL(); } From ce0286c33b6d0f6f37d90702c4800362e5c0e7d7 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 27 May 2022 20:51:34 +0300 Subject: [PATCH 05/13] SL-17474 Device context not properly released at shutdown --- indra/llwindow/llwindowwin32.cpp | 49 +++++++++++++++++++------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 1f3823509c..8635261103 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -874,21 +874,20 @@ void LLWindowWin32::close() // Restore gamma to the system values. restoreGamma(); - if (mhDC) - { - if (!ReleaseDC(mWindowHandle, mhDC)) - { - LL_WARNS("Window") << "Release of ghDC failed" << LL_ENDL; - } - mhDC = NULL; - } - LL_DEBUGS("Window") << "Destroying Window" << LL_ENDL; mWindowThread->post([=]() { if (IsWindow(mWindowHandle)) { + if (mhDC) + { + if (!ReleaseDC(mWindowHandle, mhDC)) + { + LL_WARNS("Window") << "Release of ghDC failed!" << LL_ENDL; + } + } + // Make sure we don't leave a blank toolbar button. ShowWindow(mWindowHandle, SW_HIDE); @@ -914,6 +913,7 @@ void LLWindowWin32::close() // Even though the above lambda might not yet have run, we've already // bound mWindowHandle into it by value, which should suffice for the // operations we're asking. That's the last time WE should touch it. + mhDC = NULL; mWindowHandle = NULL; mWindowThread->close(); } @@ -1506,12 +1506,10 @@ const S32 max_format = (S32)num_formats - 1; { wglDeleteContext (mhRC); // Release The Rendering Context mhRC = 0; // Zero The Rendering Context - } - ReleaseDC (mWindowHandle, mhDC); // Release The Device Context - mhDC = 0; // Zero The Device Context } + // will release and recreate mhDC, mWindowHandle recreateWindow(window_rect, dw_ex_style, dw_style); RECT rect; @@ -1661,7 +1659,8 @@ const S32 max_format = (S32)num_formats - 1; void LLWindowWin32::recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw_style) { - auto oldHandle = mWindowHandle; + auto oldWindowHandle = mWindowHandle; + auto oldDCHandle = mhDC; // zero out mWindowHandle and mhDC before destroying window so window // thread falls back to peekmessage @@ -1673,7 +1672,8 @@ void LLWindowWin32::recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw auto window_work = [this, self=mWindowThread, - oldHandle, + oldWindowHandle, + oldDCHandle, // bind CreateWindowEx() parameters by value instead of // back-referencing LLWindowWin32 members windowClassName=mWindowClassName, @@ -1689,11 +1689,20 @@ void LLWindowWin32::recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw self->mWindowHandle = 0; self->mhDC = 0; - // important to call DestroyWindow() from the window thread - if (oldHandle && !destroy_window_handler(oldHandle)) + if (oldWindowHandle) { - LL_WARNS("Window") << "Failed to properly close window before recreating it!" - << LL_ENDL; + if (oldDCHandle && !ReleaseDC(oldWindowHandle, oldDCHandle)) + { + LL_WARNS("Window") << "Failed to ReleaseDC" << LL_ENDL; + } + + // important to call DestroyWindow() from the window thread + if (!destroy_window_handler(oldWindowHandle)) + { + + LL_WARNS("Window") << "Failed to properly close window before recreating it!" + << LL_ENDL; + } } auto handle = CreateWindowEx(dw_ex_style, @@ -1731,7 +1740,7 @@ void LLWindowWin32::recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw }; // But how we pass window_work to the window thread depends on whether we // already have a window handle. - if (! oldHandle) + if (!oldWindowHandle) { // Pass window_work using the WorkQueue: without an existing window // handle, the window thread can't call GetMessage(). @@ -1744,7 +1753,7 @@ void LLWindowWin32::recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw // PostMessage(oldHandle) because oldHandle won't be destroyed until // the window thread has retrieved and executed window_work. LL_DEBUGS("Window") << "posting window_work to message queue" << LL_ENDL; - mWindowThread->Post(oldHandle, window_work); + mWindowThread->Post(oldWindowHandle, window_work); } auto future = promise.get_future(); From 0fe8493b5a531bb0fbc431f54ee25f8e2923b5bf Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Wed, 1 Jun 2022 17:42:25 +0300 Subject: [PATCH 06/13] SL-17509 The viewer spams warnings if the user is in a parcel or region with voice disabled --- indra/newview/llvoicevivox.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 19036a3f77..54f79a3a2e 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -1606,8 +1606,21 @@ bool LLVivoxVoiceClient::requestParcelVoiceInfo() } else { - LL_WARNS("Voice") << "No voice channel credentials" << LL_ENDL; - + LLVoiceChannel* channel = LLVoiceChannel::getCurrentVoiceChannel(); + if (channel != NULL) + { + if (channel->getSessionName().empty() && channel->getSessionID().isNull()) + { + if (LLViewerParcelMgr::getInstance()->allowAgentVoice()) + { + LL_WARNS("Voice") << "No channel credentials for default channel" << LL_ENDL; + } + } + else + { + LL_WARNS("Voice") << "No voice channel credentials" << LL_ENDL; + } + } } } else From 616f2b639bc6907e810664522304305337646292 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Mon, 6 Jun 2022 19:57:03 -0500 Subject: [PATCH 07/13] SL-17532 Potential fix for some rigged mesh draw order issues. --- .../shaders/class1/deferred/materialF.glsl | 13 ++-- indra/newview/lldrawable.h | 1 + indra/newview/lldrawpoolalpha.cpp | 43 ++++++++++++-- indra/newview/llspatialpartition.cpp | 59 +++++++++++++++++++ indra/newview/llspatialpartition.h | 2 +- indra/newview/llviewershadermgr.cpp | 12 ++++ indra/newview/llvovolume.cpp | 51 +++++++++++++--- 7 files changed, 161 insertions(+), 20 deletions(-) diff --git a/indra/newview/app_settings/shaders/class1/deferred/materialF.glsl b/indra/newview/app_settings/shaders/class1/deferred/materialF.glsl index 02d83925ea..b26194f278 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/materialF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/materialF.glsl @@ -202,7 +202,7 @@ VARYING vec2 vary_texcoord2; uniform float env_intensity; uniform vec4 specular_color; // specular color RGB and specular exponent (glossiness) in alpha -#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_MASK) +#ifdef HAS_ALPHA_MASK uniform float minimum_alpha; #endif @@ -227,11 +227,12 @@ void main() vec4 diffcol = texture2D(diffuseMap, vary_texcoord0.xy); diffcol.rgb *= vertex_color.rgb; -#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_MASK) - - // Comparing floats cast from 8-bit values, produces acne right at the 8-bit transition points - float bias = 0.001953125; // 1/512, or half an 8-bit quantization - if (diffcol.a < minimum_alpha-bias) +#ifdef HAS_ALPHA_MASK +#if DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_BLEND + if (diffcol.a*vertex_color.a < minimum_alpha) +#else + if (diffcol.a < minimum_alpha) +#endif { discard; } diff --git a/indra/newview/lldrawable.h b/indra/newview/lldrawable.h index 2372ea01a6..2a0f4c93ac 100644 --- a/indra/newview/lldrawable.h +++ b/indra/newview/lldrawable.h @@ -133,6 +133,7 @@ public: inline LLFace* getFace(const S32 i) const; inline S32 getNumFaces() const; face_list_t& getFaces() { return mFaces; } + const face_list_t& getFaces() const { return mFaces; } //void removeFace(const S32 i); // SJB: Avoid using this, it's slow LLFace* addFace(LLFacePool *poolp, LLViewerTexture *texturep); diff --git a/indra/newview/lldrawpoolalpha.cpp b/indra/newview/lldrawpoolalpha.cpp index e674707c01..70ffd85608 100644 --- a/indra/newview/lldrawpoolalpha.cpp +++ b/indra/newview/lldrawpoolalpha.cpp @@ -50,6 +50,8 @@ #include "llglcommonfunc.h" #include "llvoavatar.h" +#pragma optimize("", off) + BOOL LLDrawPoolAlpha::sShowDebugAlpha = FALSE; #define current_shader (LLGLSLShader::sCurBoundShaderPtr) @@ -58,8 +60,11 @@ static BOOL deferred_render = FALSE; // minimum alpha before discarding a fragment static const F32 MINIMUM_ALPHA = 0.004f; // ~ 1/255 +static const F32 MINIMUM_RIGGED_ALPHA = MINIMUM_ALPHA; + // minimum alpha before discarding a fragment when rendering impostors static const F32 MINIMUM_IMPOSTOR_ALPHA = 0.1f; +static const F32 MINIMUM_IMPOSTOR_RIGGED_ALPHA = 0.1f; LLDrawPoolAlpha::LLDrawPoolAlpha(U32 type) : LLRenderPass(type), target_shader(NULL), @@ -111,11 +116,25 @@ static void prepare_alpha_shader(LLGLSLShader* shader, bool textureGamma, bool d if (LLPipeline::sImpostorRender) { - shader->setMinimumAlpha(MINIMUM_IMPOSTOR_ALPHA); + if (shader->mRiggedVariant == shader) + { + shader->setMinimumAlpha(MINIMUM_IMPOSTOR_RIGGED_ALPHA); + } + else + { + shader->setMinimumAlpha(MINIMUM_IMPOSTOR_ALPHA); + } } else { - shader->setMinimumAlpha(MINIMUM_ALPHA); + if (shader->mRiggedVariant == shader) + { + shader->setMinimumAlpha(MINIMUM_RIGGED_ALPHA); + } + else + { + shader->setMinimumAlpha(MINIMUM_ALPHA); + } } if (textureGamma) { @@ -147,6 +166,10 @@ void LLDrawPoolAlpha::renderPostDeferred(S32 pass) (LLPipeline::sUnderWaterRender) ? &gDeferredAlphaWaterProgram : &gDeferredAlphaProgram; prepare_alpha_shader(simple_shader, false, true); //prime simple shader (loads shadow relevant uniforms) + for (int i = 0; i < LLMaterial::SHADER_COUNT; ++i) + { + prepare_alpha_shader(LLPipeline::sUnderWaterRender ? &gDeferredMaterialWaterProgram[i] : &gDeferredMaterialProgram[i], false, false); // note: bindDeferredShader will get called during render loop for materials + } // first pass, render rigged objects only and render to depth buffer forwardRender(true); @@ -184,7 +207,7 @@ void LLDrawPoolAlpha::renderPostDeferred(S32 pass) } //set some generic parameters for forward (non-deferred) rendering -static void prepare_forward_shader(LLGLSLShader* shader, F32 minimum_alpha) +static void prepare_forward_shader(LLGLSLShader* shader, F32 minimum_alpha, F32 minimum_rigged_alpha) { shader->bind(); shader->setMinimumAlpha(minimum_alpha); @@ -193,7 +216,7 @@ static void prepare_forward_shader(LLGLSLShader* shader, F32 minimum_alpha) //also prepare rigged variant if (shader->mRiggedVariant && shader->mRiggedVariant != shader) { - prepare_forward_shader(shader->mRiggedVariant, minimum_alpha); + prepare_forward_shader(shader->mRiggedVariant, minimum_rigged_alpha, minimum_rigged_alpha); } } @@ -211,12 +234,20 @@ void LLDrawPoolAlpha::render(S32 pass) (LLPipeline::sUnderWaterRender) ? &gObjectEmissiveWaterProgram : &gObjectEmissiveProgram; F32 minimum_alpha = MINIMUM_ALPHA; + F32 minimum_rigged_alpha = MINIMUM_RIGGED_ALPHA; if (LLPipeline::sImpostorRender) { minimum_alpha = MINIMUM_IMPOSTOR_ALPHA; + minimum_rigged_alpha = MINIMUM_IMPOSTOR_RIGGED_ALPHA; + } + + prepare_forward_shader(fullbright_shader, minimum_alpha, minimum_rigged_alpha); + prepare_forward_shader(simple_shader, minimum_alpha, minimum_rigged_alpha); + + for (int i = 0; i < LLMaterial::SHADER_COUNT; ++i) + { + prepare_forward_shader(LLPipeline::sUnderWaterRender ? &gDeferredMaterialWaterProgram[i] : &gDeferredMaterialProgram[i], minimum_alpha, minimum_rigged_alpha); } - prepare_forward_shader(fullbright_shader, minimum_alpha); - prepare_forward_shader(simple_shader, minimum_alpha); //first pass -- rigged only and drawn to depth buffer forwardRender(true); diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp index 0d53950889..c16a1854be 100644 --- a/indra/newview/llspatialpartition.cpp +++ b/indra/newview/llspatialpartition.cpp @@ -1567,6 +1567,62 @@ void pushVertsColorCoded(LLSpatialGroup* group, U32 mask) } } +// return false if drawable is rigged and: +// - a linked rigged drawable has a different spatial group +// - a linked rigged drawable face has the wrong draw order index +bool check_rigged_group(LLDrawable* drawable) +{ + if (drawable->isState(LLDrawable::RIGGED)) + { + LLSpatialGroup* group = drawable->getSpatialGroup(); + LLDrawable* root = drawable->getRoot(); + + if (root->isState(LLDrawable::RIGGED) && root->getSpatialGroup() != group) + { + llassert(false); + return false; + } + + S32 last_draw_index = -1; + if (root->isState(LLDrawable::RIGGED)) + { + for (auto& face : root->getFaces()) + { + if ((S32) face->getDrawOrderIndex() <= last_draw_index) + { + llassert(false); + return false; + } + last_draw_index = face->getDrawOrderIndex(); + } + } + + for (auto& child : root->getVObj()->getChildren()) + { + if (child->mDrawable->isState(LLDrawable::RIGGED)) + { + for (auto& face : child->mDrawable->getFaces()) + { + if ((S32) face->getDrawOrderIndex() <= last_draw_index) + { + llassert(false); + return false; + } + last_draw_index = face->getDrawOrderIndex(); + } + } + + if (child->mDrawable->getSpatialGroup() != group) + { + llassert(false); + return false; + } + } + } + + return true; +} + void renderOctree(LLSpatialGroup* group) { //render solid object bounding box, color @@ -1611,6 +1667,9 @@ void renderOctree(LLSpatialGroup* group) { continue; } + + llassert(check_rigged_group(drawable)); + if (!group->getSpatialPartition()->isBridge()) { gGL.pushMatrix(); diff --git a/indra/newview/llspatialpartition.h b/indra/newview/llspatialpartition.h index 6d3ef33801..f7bc9fb644 100644 --- a/indra/newview/llspatialpartition.h +++ b/indra/newview/llspatialpartition.h @@ -264,7 +264,7 @@ public: return lhs->mAvatarp < rhs->mAvatarp; } - return lhs->mRenderOrder > rhs->mRenderOrder; + return lhs->mRenderOrder < rhs->mRenderOrder; } }; diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index 086b433c72..e926c5c3ba 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -1484,6 +1484,12 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() gDeferredMaterialProgram[i].addPermutation("DIFFUSE_ALPHA_MODE", llformat("%d", alpha_mode)); + if (alpha_mode != 0) + { + gDeferredMaterialProgram[i].mFeatures.hasAlphaMask = true; + gDeferredMaterialProgram[i].addPermutation("HAS_ALPHA_MASK", "1"); + } + if (use_sun_shadow) { gDeferredMaterialProgram[i].addPermutation("HAS_SUN_SHADOW", "1"); @@ -1542,6 +1548,12 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() } gDeferredMaterialWaterProgram[i].addPermutation("DIFFUSE_ALPHA_MODE", llformat("%d", alpha_mode)); + if (alpha_mode != 0) + { + gDeferredMaterialWaterProgram[i].mFeatures.hasAlphaMask = true; + gDeferredMaterialWaterProgram[i].addPermutation("HAS_ALPHA_MASK", "1"); + } + if (use_sun_shadow) { gDeferredMaterialWaterProgram[i].addPermutation("HAS_SUN_SHADOW", "1"); diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 126a25115d..eb1e2cbd80 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -5480,7 +5480,7 @@ static inline void add_face(T*** list, U32* count, T* face) { if (count[1] < MAX_FACE_COUNT) { - face->setDrawOrderIndex(count[1]); + //face->setDrawOrderIndex(count[1]); list[1][count[1]++] = face; } } @@ -5488,15 +5488,40 @@ static inline void add_face(T*** list, U32* count, T* face) { if (count[0] < MAX_FACE_COUNT) { - face->setDrawOrderIndex(count[0]); + //face->setDrawOrderIndex(count[0]); list[0][count[0]++] = face; } } } +// return index into linkset for given object (0 for root prim) +U32 get_linkset_index(LLVOVolume* vobj) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWABLE; + if (vobj->isRootEdit()) + { + return 0; + } + + LLViewerObject* root = vobj->getRootEdit(); + U32 idx = 1; + for (auto& child : root->getChildren()) + { + if (child == vobj) + { + return idx; + } + ++idx; + } + + llassert(false); + return idx; //should never get here +} + void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) { LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; + if (group->changeLOD()) { group->mLastUpdateDistance = group->mDistance; @@ -5655,6 +5680,8 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) avatar->addAttachmentOverridesForObject(vobj, NULL, false); } + U32 linkset_index = get_linkset_index(vobj); + // Standard rigged mesh attachments: bool rigged = !vobj->isAnimatedObject() && skinInfo && vobj->isAttachment(); // Animated objects. Have to check for isRiggedMesh() to @@ -5674,6 +5701,9 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) continue; } + // order by linkset index first and face index second + facep->setDrawOrderIndex(linkset_index * 100 + i); + //ALWAYS null out vertex buffer on rebuild -- if the face lands in a render // batch, it will recover its vertex buffer reference from the spatial group facep->setVertexBuffer(NULL); @@ -5698,11 +5728,6 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) if (facep->isState(LLFace::RIGGED)) { //face is not rigged but used to be, remove from rigged face pool - LLDrawPoolAvatar* pool = (LLDrawPoolAvatar*) facep->getPool(); - if (pool) - { - pool->removeFace(facep); - } facep->clearState(LLFace::RIGGED); facep->mAvatar = NULL; facep->mSkinInfo = NULL; @@ -6158,6 +6183,13 @@ struct CompareBatchBreakerRigged } }; +struct CompareDrawOrder +{ + bool operator()(const LLFace* const& lhs, const LLFace* const& rhs) + { + return lhs->getDrawOrderIndex() < rhs->getDrawOrderIndex(); + } +}; U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace** faces, U32 face_count, BOOL distance_sort, BOOL batch_textures, BOOL rigged) { @@ -6202,6 +6234,11 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace //sort faces by things that break batches, including avatar and mesh id std::sort(faces, faces + face_count, CompareBatchBreakerRigged()); } + else + { + // preserve legacy draw order for rigged faces + std::sort(faces, faces + face_count, CompareDrawOrder()); + } } else if (!distance_sort) { From 3e1db1f18405d50a63d772722175c859c1952f92 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Tue, 7 Jun 2022 10:36:59 -0500 Subject: [PATCH 08/13] SL-17532 Cleanup from hasty commit (thanks to Henri for the prodding). --- indra/newview/lldrawpoolalpha.cpp | 34 +++++++------------------------ indra/newview/llvovolume.cpp | 2 +- 2 files changed, 8 insertions(+), 28 deletions(-) diff --git a/indra/newview/lldrawpoolalpha.cpp b/indra/newview/lldrawpoolalpha.cpp index 70ffd85608..6c1abb24c9 100644 --- a/indra/newview/lldrawpoolalpha.cpp +++ b/indra/newview/lldrawpoolalpha.cpp @@ -50,8 +50,6 @@ #include "llglcommonfunc.h" #include "llvoavatar.h" -#pragma optimize("", off) - BOOL LLDrawPoolAlpha::sShowDebugAlpha = FALSE; #define current_shader (LLGLSLShader::sCurBoundShaderPtr) @@ -60,11 +58,9 @@ static BOOL deferred_render = FALSE; // minimum alpha before discarding a fragment static const F32 MINIMUM_ALPHA = 0.004f; // ~ 1/255 -static const F32 MINIMUM_RIGGED_ALPHA = MINIMUM_ALPHA; // minimum alpha before discarding a fragment when rendering impostors static const F32 MINIMUM_IMPOSTOR_ALPHA = 0.1f; -static const F32 MINIMUM_IMPOSTOR_RIGGED_ALPHA = 0.1f; LLDrawPoolAlpha::LLDrawPoolAlpha(U32 type) : LLRenderPass(type), target_shader(NULL), @@ -116,25 +112,11 @@ static void prepare_alpha_shader(LLGLSLShader* shader, bool textureGamma, bool d if (LLPipeline::sImpostorRender) { - if (shader->mRiggedVariant == shader) - { - shader->setMinimumAlpha(MINIMUM_IMPOSTOR_RIGGED_ALPHA); - } - else - { - shader->setMinimumAlpha(MINIMUM_IMPOSTOR_ALPHA); - } + shader->setMinimumAlpha(MINIMUM_IMPOSTOR_ALPHA); } else { - if (shader->mRiggedVariant == shader) - { - shader->setMinimumAlpha(MINIMUM_RIGGED_ALPHA); - } - else - { - shader->setMinimumAlpha(MINIMUM_ALPHA); - } + shader->setMinimumAlpha(MINIMUM_ALPHA); } if (textureGamma) { @@ -207,7 +189,7 @@ void LLDrawPoolAlpha::renderPostDeferred(S32 pass) } //set some generic parameters for forward (non-deferred) rendering -static void prepare_forward_shader(LLGLSLShader* shader, F32 minimum_alpha, F32 minimum_rigged_alpha) +static void prepare_forward_shader(LLGLSLShader* shader, F32 minimum_alpha) { shader->bind(); shader->setMinimumAlpha(minimum_alpha); @@ -216,7 +198,7 @@ static void prepare_forward_shader(LLGLSLShader* shader, F32 minimum_alpha, F32 //also prepare rigged variant if (shader->mRiggedVariant && shader->mRiggedVariant != shader) { - prepare_forward_shader(shader->mRiggedVariant, minimum_rigged_alpha, minimum_rigged_alpha); + prepare_forward_shader(shader->mRiggedVariant, minimum_alpha); } } @@ -234,19 +216,17 @@ void LLDrawPoolAlpha::render(S32 pass) (LLPipeline::sUnderWaterRender) ? &gObjectEmissiveWaterProgram : &gObjectEmissiveProgram; F32 minimum_alpha = MINIMUM_ALPHA; - F32 minimum_rigged_alpha = MINIMUM_RIGGED_ALPHA; if (LLPipeline::sImpostorRender) { minimum_alpha = MINIMUM_IMPOSTOR_ALPHA; - minimum_rigged_alpha = MINIMUM_IMPOSTOR_RIGGED_ALPHA; } - prepare_forward_shader(fullbright_shader, minimum_alpha, minimum_rigged_alpha); - prepare_forward_shader(simple_shader, minimum_alpha, minimum_rigged_alpha); + prepare_forward_shader(fullbright_shader, minimum_alpha); + prepare_forward_shader(simple_shader, minimum_alpha); for (int i = 0; i < LLMaterial::SHADER_COUNT; ++i) { - prepare_forward_shader(LLPipeline::sUnderWaterRender ? &gDeferredMaterialWaterProgram[i] : &gDeferredMaterialProgram[i], minimum_alpha, minimum_rigged_alpha); + prepare_forward_shader(LLPipeline::sUnderWaterRender ? &gDeferredMaterialWaterProgram[i] : &gDeferredMaterialProgram[i], minimum_alpha); } //first pass -- rigged only and drawn to depth buffer diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index eb1e2cbd80..f47fb6287c 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -5505,7 +5505,7 @@ U32 get_linkset_index(LLVOVolume* vobj) LLViewerObject* root = vobj->getRootEdit(); U32 idx = 1; - for (auto& child : root->getChildren()) + for (const auto& child : root->getChildren()) { if (child == vobj) { From f96659d7b0661455c8a29f9186f9c96484b6c193 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Wed, 8 Jun 2022 18:24:48 +0300 Subject: [PATCH 09/13] SL-17552 Clear dirty flag when clicking Save button --- indra/newview/llfloatereditextdaycycle.cpp | 1 + indra/newview/llfloaterfixedenvironment.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/indra/newview/llfloatereditextdaycycle.cpp b/indra/newview/llfloatereditextdaycycle.cpp index 24673d5a7c..297ad24359 100644 --- a/indra/newview/llfloatereditextdaycycle.cpp +++ b/indra/newview/llfloatereditextdaycycle.cpp @@ -665,6 +665,7 @@ void LLFloaterEditExtDayCycle::onButtonApply(LLUICtrl *ctrl, const LLSD &data) if (ctrl_action == ACTION_SAVE) { doApplyUpdateInventory(dayclone); + clearDirtyFlag(); } else if (ctrl_action == ACTION_SAVEAS) { diff --git a/indra/newview/llfloaterfixedenvironment.cpp b/indra/newview/llfloaterfixedenvironment.cpp index fec218ca3b..aa9a2c164a 100644 --- a/indra/newview/llfloaterfixedenvironment.cpp +++ b/indra/newview/llfloaterfixedenvironment.cpp @@ -296,6 +296,7 @@ void LLFloaterFixedEnvironment::onButtonApply(LLUICtrl *ctrl, const LLSD &data) if (ctrl_action == ACTION_SAVE) { doApplyUpdateInventory(setting_clone); + clearDirtyFlag(); } else if (ctrl_action == ACTION_SAVEAS) { From 14ea73033ab64321d4034714ece6ad999093a9a8 Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Tue, 31 May 2022 12:49:53 -0700 Subject: [PATCH 10/13] SL-17483: Make ThreadPool inherit LLInstanceTracker (cherry picked from commit 41d6a0e222241606c317281e2f0b211e16813dd5) --- indra/llcommon/threadpool.cpp | 1 + indra/llcommon/threadpool.h | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/indra/llcommon/threadpool.cpp b/indra/llcommon/threadpool.cpp index ba914035e2..d5adf11264 100644 --- a/indra/llcommon/threadpool.cpp +++ b/indra/llcommon/threadpool.cpp @@ -22,6 +22,7 @@ #include "stringize.h" LL::ThreadPool::ThreadPool(const std::string& name, size_t threads, size_t capacity): + super(name), mQueue(name, capacity), mName("ThreadPool:" + name), mThreadCount(threads) diff --git a/indra/llcommon/threadpool.h b/indra/llcommon/threadpool.h index b79c9b9090..f8eec3b457 100644 --- a/indra/llcommon/threadpool.h +++ b/indra/llcommon/threadpool.h @@ -22,8 +22,10 @@ namespace LL { - class ThreadPool + class ThreadPool: public LLInstanceTracker { + private: + using super = LLInstanceTracker; public: /** * Pass ThreadPool a string name. This can be used to look up the From 3e60b53d52d09e3f1412a9476743a77e555104d9 Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Fri, 6 May 2022 14:46:49 -0700 Subject: [PATCH 11/13] SL-16182: Offload audio decoding from the main thread to the general work queue. The work queue callback binds "this". This is deemed safe due to current dependencies, but see the associated comment in the return callback. There was some trial and error to get a this-binded lambda to compile. Due to LLVorbisDecodeState writing to disk off-thread, limit audio decodes proportional to general worker thread count. Guess the thread count for now. --- indra/llaudio/llaudiodecodemgr.cpp | 319 +++++++++++++-------- indra/llaudio/llaudiodecodemgr.h | 15 +- indra/llaudio/llaudioengine.cpp | 105 +++---- indra/llaudio/llaudioengine.h | 59 ++-- indra/llaudio/llaudioengine_fmodstudio.cpp | 14 +- indra/llaudio/llaudioengine_fmodstudio.h | 2 +- indra/llaudio/llaudioengine_openal.cpp | 4 +- indra/llcommon/workqueue.h | 8 +- indra/newview/llappviewer.cpp | 3 +- indra/newview/llstartup.cpp | 2 +- indra/newview/llvieweraudio.h | 2 - 11 files changed, 308 insertions(+), 225 deletions(-) mode change 100644 => 100755 indra/llaudio/llaudiodecodemgr.cpp mode change 100644 => 100755 indra/llaudio/llaudioengine.h diff --git a/indra/llaudio/llaudiodecodemgr.cpp b/indra/llaudio/llaudiodecodemgr.cpp old mode 100644 new mode 100755 index ff0aa6e76e..cc5abee08a --- a/indra/llaudio/llaudiodecodemgr.cpp +++ b/indra/llaudio/llaudiodecodemgr.cpp @@ -35,6 +35,7 @@ #include "llendianswizzle.h" #include "llassetstorage.h" #include "llrefcount.h" +#include "workqueue.h" #include "llvorbisencode.h" @@ -45,15 +46,13 @@ extern LLAudioEngine *gAudiop; -LLAudioDecodeMgr *gAudioDecodeMgrp = NULL; - static const S32 WAV_HEADER_SIZE = 44; ////////////////////////////////////////////////////////////////////////////// -class LLVorbisDecodeState : public LLRefCount +class LLVorbisDecodeState : public LLThreadSafeRefCount { public: class WriteResponder : public LLLFSThread::Responder @@ -532,146 +531,242 @@ void LLVorbisDecodeState::flushBadFile() class LLAudioDecodeMgr::Impl { - friend class LLAudioDecodeMgr; -public: - Impl() {}; - ~Impl() {}; + friend class LLAudioDecodeMgr; + LLAudioDecodeMgr::Impl(); + public: - void processQueue(const F32 num_secs = 0.005); + void processQueue(); -protected: - std::deque mDecodeQueue; - LLPointer mCurrentDecodep; + void startMoreDecodes(); + void enqueueFinishAudio(const LLUUID &decode_id, LLPointer& decode_state); + void checkDecodesFinished(); + + protected: + std::deque mDecodeQueue; + std::map> mDecodes; }; - -void LLAudioDecodeMgr::Impl::processQueue(const F32 num_secs) +LLAudioDecodeMgr::Impl::Impl() { - LLUUID uuid; +} - LLTimer decode_timer; +// Returns the in-progress decode_state, which may be an empty LLPointer if +// there was an error and there is no more work to be done. +LLPointer beginDecodingAndWritingAudio(const LLUUID &decode_id); - BOOL done = FALSE; - while (!done) - { - if (mCurrentDecodep) - { - BOOL res; +// Return true if finished +bool tryFinishAudio(const LLUUID &decode_id, LLPointer decode_state); - // Decode in a loop until we're done or have run out of time. - while(!(res = mCurrentDecodep->decodeSection()) && (decode_timer.getElapsedTimeF32() < num_secs)) - { - // decodeSection does all of the work above - } +void LLAudioDecodeMgr::Impl::processQueue() +{ + // First, check if any audio from in-progress decodes are ready to play. If + // so, mark them ready for playback (or errored, in case of error). + checkDecodesFinished(); - if (mCurrentDecodep->isDone() && !mCurrentDecodep->isValid()) - { - // We had an error when decoding, abort. - LL_WARNS("AudioEngine") << mCurrentDecodep->getUUID() << " has invalid vorbis data, aborting decode" << LL_ENDL; - mCurrentDecodep->flushBadFile(); + // Second, start as many decodes from the queue as permitted + startMoreDecodes(); +} - if (gAudiop) - { - LLAudioData *adp = gAudiop->getAudioData(mCurrentDecodep->getUUID()); - adp->setHasValidData(false); - adp->setHasCompletedDecode(true); - } +void LLAudioDecodeMgr::Impl::startMoreDecodes() +{ + llassert_always(gAudiop); - mCurrentDecodep = NULL; - done = TRUE; - } + LL::WorkQueue::ptr_t main_queue = LL::WorkQueue::getInstance("mainloop"); + // *NOTE: main_queue->postTo casts this refcounted smart pointer to a weak + // pointer + LL::WorkQueue::ptr_t general_queue = LL::WorkQueue::getInstance("General"); + llassert_always(main_queue); + llassert_always(general_queue); + // Set max decodes to double the thread count of the general work queue. + // This ensures the general work queue is full, but prevents theoretical + // buildup of buffers in memory due to disk writes once the + // LLVorbisDecodeState leaves the worker thread (see + // LLLFSThread::sLocal->write). This is probably as fast as we can get it + // without modifying/removing LLVorbisDecodeState, at which point we should + // consider decoding the audio during the asset download process. + // -Cosmic,2022-05-11 + const size_t MAX_DECODES = 4 * 2; - if (!res) - { - // We've used up out time slice, bail... - done = TRUE; - } - else if (mCurrentDecodep) - { - if (gAudiop && mCurrentDecodep->finishDecode()) - { - // We finished! - LLAudioData *adp = gAudiop->getAudioData(mCurrentDecodep->getUUID()); - if (!adp) - { - LL_WARNS("AudioEngine") << "Missing LLAudioData for decode of " << mCurrentDecodep->getUUID() << LL_ENDL; - } - else if (mCurrentDecodep->isValid() && mCurrentDecodep->isDone()) - { - adp->setHasCompletedDecode(true); - adp->setHasDecodedData(true); - adp->setHasValidData(true); + while (!mDecodeQueue.empty() && mDecodes.size() < MAX_DECODES) + { + const LLUUID decode_id = mDecodeQueue.front(); + mDecodeQueue.pop_front(); - // At this point, we could see if anyone needs this sound immediately, but - // I'm not sure that there's a reason to - we need to poll all of the playing - // sounds anyway. - //LL_INFOS("AudioEngine") << "Finished the vorbis decode, now what?" << LL_ENDL; - } - else - { - adp->setHasCompletedDecode(true); - LL_INFOS("AudioEngine") << "Vorbis decode failed for " << mCurrentDecodep->getUUID() << LL_ENDL; - } - mCurrentDecodep = NULL; - } - done = TRUE; // done for now - } - } + // Don't decode the same file twice + if (mDecodes.find(decode_id) != mDecodes.end()) + { + continue; + } + if (gAudiop->hasDecodedFile(decode_id)) + { + continue; + } - if (!done) - { - if (mDecodeQueue.empty()) - { - // Nothing else on the queue. - done = TRUE; - } - else - { - LLUUID uuid; - uuid = mDecodeQueue.front(); - mDecodeQueue.pop_front(); - if (!gAudiop || gAudiop->hasDecodedFile(uuid)) - { - // This file has already been decoded, don't decode it again. - continue; - } + // Kick off a decode + mDecodes[decode_id] = LLPointer(NULL); + main_queue->postTo( + general_queue, + [decode_id, this]() // Work done on general queue + { + LLPointer decode_state = beginDecodingAndWritingAudio(decode_id); - LL_DEBUGS() << "Decoding " << uuid << " from audio queue!" << LL_ENDL; + if (!decode_state) + { + // Audio decode has errored + return decode_state; + } - std::string uuid_str; - std::string d_path; + // Disk write of decoded audio is now in progress off-thread + return decode_state; + }, + [decode_id, this](LLPointer 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; + } - LLTimer timer; - timer.reset(); + // 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. - uuid.toString(uuid_str); - d_path = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str) + ".dsf"; + enqueueFinishAudio(decode_id, decode_state); + }); + } +} - mCurrentDecodep = new LLVorbisDecodeState(uuid, d_path); - if (!mCurrentDecodep->initDecode()) - { - mCurrentDecodep = NULL; - } - } - } - } +LLPointer beginDecodingAndWritingAudio(const LLUUID &decode_id) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_MEDIA; + + LL_DEBUGS() << "Decoding " << decode_id << " from audio queue!" << LL_ENDL; + + std::string d_path = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, decode_id.asString()) + ".dsf"; + LLPointer decode_state = new LLVorbisDecodeState(decode_id, d_path); + + if (!decode_state->initDecode()) + { + return NULL; + } + + // Decode in a loop until we're done + while (!decode_state->decodeSection()) + { + // decodeSection does all of the work above + } + + if (!decode_state->isDone()) + { + // Decode stopped early, or something bad happened to the file + // during decoding. + LL_WARNS("AudioEngine") << decode_id << " has invalid vorbis data or decode has been canceled, aborting decode" << LL_ENDL; + decode_state->flushBadFile(); + return NULL; + } + + if (!decode_state->isValid()) + { + // We had an error when decoding, abort. + LL_WARNS("AudioEngine") << decode_id << " has invalid vorbis data, aborting decode" << LL_ENDL; + decode_state->flushBadFile(); + return NULL; + } + + // Kick off the writing of the decoded audio to the disk cache. + // The receiving thread can then cheaply call finishDecode() again to check + // if writing has finished. Someone has to hold on to the refcounted + // decode_state to prevent it from getting destroyed during write. + decode_state->finishDecode(); + + return decode_state; +} + +void LLAudioDecodeMgr::Impl::enqueueFinishAudio(const LLUUID &decode_id, LLPointer& decode_state) +{ + // Assumed fast + if (tryFinishAudio(decode_id, decode_state)) + { + // Done early! + auto decode_iter = mDecodes.find(decode_id); + llassert(decode_iter != mDecodes.end()); + mDecodes.erase(decode_iter); + return; + } + + // Not done yet... enqueue it + mDecodes[decode_id] = decode_state; +} + +void LLAudioDecodeMgr::Impl::checkDecodesFinished() +{ + auto decode_iter = mDecodes.begin(); + while (decode_iter != mDecodes.end()) + { + const LLUUID& decode_id = decode_iter->first; + const LLPointer& decode_state = decode_iter->second; + if (tryFinishAudio(decode_id, decode_state)) + { + decode_iter = mDecodes.erase(decode_iter); + } + else + { + ++decode_iter; + } + } +} + +bool tryFinishAudio(const LLUUID &decode_id, LLPointer decode_state) +{ + // decode_state is a file write in progress unless finished is true + bool finished = decode_state && decode_state->finishDecode(); + if (!finished) + { + return false; + } + + llassert_always(gAudiop); + + LLAudioData *adp = gAudiop->getAudioData(decode_id); + if (!adp) + { + LL_WARNS("AudioEngine") << "Missing LLAudioData for decode of " << decode_id << LL_ENDL; + return true; + } + + bool valid = decode_state && decode_state->isValid(); + // Mark current decode finished regardless of success or failure + adp->setHasCompletedDecode(true); + // Flip flags for decoded data + adp->setHasDecodeFailed(!valid); + adp->setHasDecodedData(valid); + // When finished decoding, there will also be a decoded wav file cached on + // disk with the .dsf extension + if (valid) + { + adp->setHasWAVLoadFailed(false); + } + + return true; } ////////////////////////////////////////////////////////////////////////////// LLAudioDecodeMgr::LLAudioDecodeMgr() { - mImpl = new Impl; + mImpl = new Impl(); } LLAudioDecodeMgr::~LLAudioDecodeMgr() { - delete mImpl; + delete mImpl; + mImpl = nullptr; } -void LLAudioDecodeMgr::processQueue(const F32 num_secs) +void LLAudioDecodeMgr::processQueue() { - mImpl->processQueue(num_secs); + mImpl->processQueue(); } BOOL LLAudioDecodeMgr::addDecodeRequest(const LLUUID &uuid) @@ -687,7 +782,7 @@ BOOL LLAudioDecodeMgr::addDecodeRequest(const LLUUID &uuid) { // Just put it on the decode queue. LL_DEBUGS("AudioEngine") << "addDecodeRequest for " << uuid << " has local asset file already" << LL_ENDL; - mImpl->mDecodeQueue.push_back(uuid); + mImpl->mDecodeQueue.push_back(uuid); return TRUE; } diff --git a/indra/llaudio/llaudiodecodemgr.h b/indra/llaudio/llaudiodecodemgr.h index ceaff3f2d8..4c17b46156 100644 --- a/indra/llaudio/llaudiodecodemgr.h +++ b/indra/llaudio/llaudiodecodemgr.h @@ -32,24 +32,23 @@ #include "llassettype.h" #include "llframetimer.h" +#include "llsingleton.h" +template class LLPointer; class LLVorbisDecodeState; -class LLAudioDecodeMgr +class LLAudioDecodeMgr : public LLSingleton { + LLSINGLETON(LLAudioDecodeMgr); + ~LLAudioDecodeMgr(); public: - LLAudioDecodeMgr(); - ~LLAudioDecodeMgr(); - - void processQueue(const F32 num_secs = 0.005); + void processQueue(); BOOL addDecodeRequest(const LLUUID &uuid); void addAudioRequest(const LLUUID &uuid); protected: class Impl; - Impl* mImpl; + Impl* mImpl; }; -extern LLAudioDecodeMgr *gAudioDecodeMgrp; - #endif diff --git a/indra/llaudio/llaudioengine.cpp b/indra/llaudio/llaudioengine.cpp index e0ebbb76bd..cae6bb62d9 100644 --- a/indra/llaudio/llaudioengine.cpp +++ b/indra/llaudio/llaudioengine.cpp @@ -83,18 +83,10 @@ void LLAudioEngine::setDefaults() mLastStatus = 0; - mNumChannels = 0; mEnableWind = false; - S32 i; - for (i = 0; i < MAX_CHANNELS; i++) - { - mChannels[i] = NULL; - } - for (i = 0; i < MAX_BUFFERS; i++) - { - mBuffers[i] = NULL; - } + mChannels.fill(nullptr); + mBuffers.fill(nullptr); mMasterGain = 1.f; // Setting mInternalGain to an out of range value fixes the issue reported in STORM-830. @@ -111,18 +103,14 @@ void LLAudioEngine::setDefaults() } -bool LLAudioEngine::init(const S32 num_channels, void* userdata, const std::string &app_title) +bool LLAudioEngine::init(void* userdata, const std::string &app_title) { setDefaults(); - mNumChannels = num_channels; mUserData = userdata; allocateListener(); - // Initialize the decode manager - gAudioDecodeMgrp = new LLAudioDecodeMgr; - LL_INFOS("AudioEngine") << "LLAudioEngine::init() AudioEngine successfully initialized" << LL_ENDL; return true; @@ -131,10 +119,6 @@ bool LLAudioEngine::init(const S32 num_channels, void* userdata, const std::stri void LLAudioEngine::shutdown() { - // Clean up decode manager - delete gAudioDecodeMgrp; - gAudioDecodeMgrp = NULL; - // Clean up wind source cleanupWind(); @@ -156,14 +140,14 @@ void LLAudioEngine::shutdown() // Clean up channels S32 i; - for (i = 0; i < MAX_CHANNELS; i++) + for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++) { delete mChannels[i]; mChannels[i] = NULL; } // Clean up buffers - for (i = 0; i < MAX_BUFFERS; i++) + for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++) { delete mBuffers[i]; mBuffers[i] = NULL; @@ -229,7 +213,7 @@ std::string LLAudioEngine::getInternetStreamURL() void LLAudioEngine::updateChannels() { S32 i; - for (i = 0; i < MAX_CHANNELS; i++) + for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++) { if (mChannels[i]) { @@ -240,20 +224,14 @@ void LLAudioEngine::updateChannels() } } -static const F32 default_max_decode_time = .002f; // 2 ms -void LLAudioEngine::idle(F32 max_decode_time) +void LLAudioEngine::idle() { - if (max_decode_time <= 0.f) - { - max_decode_time = default_max_decode_time; - } - // "Update" all of our audio sources, clean up dead ones. // Primarily does position updating, cleanup of unused audio sources. // Also does regeneration of the current priority of each audio source. S32 i; - for (i = 0; i < MAX_BUFFERS; i++) + for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++) { if (mBuffers[i]) { @@ -473,7 +451,7 @@ void LLAudioEngine::idle(F32 max_decode_time) commitDeferredChanges(); // Flush unused buffers that are stale enough - for (i = 0; i < MAX_BUFFERS; i++) + for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++) { if (mBuffers[i]) { @@ -489,7 +467,7 @@ void LLAudioEngine::idle(F32 max_decode_time) // Clear all of the looped flags for the channels - for (i = 0; i < MAX_CHANNELS; i++) + for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++) { if (mChannels[i]) { @@ -498,7 +476,7 @@ void LLAudioEngine::idle(F32 max_decode_time) } // Decode audio files - gAudioDecodeMgrp->processQueue(max_decode_time); + LLAudioDecodeMgr::getInstance()->processQueue(); // Call this every frame, just in case we somehow // missed picking it up in all the places that can add @@ -532,7 +510,7 @@ bool LLAudioEngine::updateBufferForData(LLAudioData *adp, const LLUUID &audio_uu { if (audio_uuid.notNull()) { - gAudioDecodeMgrp->addDecodeRequest(audio_uuid); + LLAudioDecodeMgr::getInstance()->addDecodeRequest(audio_uuid); } } else @@ -561,7 +539,7 @@ void LLAudioEngine::enableWind(bool enable) LLAudioBuffer * LLAudioEngine::getFreeBuffer() { S32 i; - for (i = 0; i < MAX_BUFFERS; i++) + for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++) { if (!mBuffers[i]) { @@ -574,7 +552,7 @@ LLAudioBuffer * LLAudioEngine::getFreeBuffer() // Grab the oldest unused buffer F32 max_age = -1.f; S32 buffer_id = -1; - for (i = 0; i < MAX_BUFFERS; i++) + for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++) { if (mBuffers[i]) { @@ -605,7 +583,7 @@ LLAudioBuffer * LLAudioEngine::getFreeBuffer() LLAudioChannel * LLAudioEngine::getFreeChannel(const F32 priority) { S32 i; - for (i = 0; i < mNumChannels; i++) + for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++) { if (!mChannels[i]) { @@ -633,7 +611,7 @@ LLAudioChannel * LLAudioEngine::getFreeChannel(const F32 priority) F32 min_priority = 10000.f; LLAudioChannel *min_channelp = NULL; - for (i = 0; i < mNumChannels; i++) + for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++) { LLAudioChannel *channelp = mChannels[i]; LLAudioSource *sourcep = channelp->getSource(); @@ -660,7 +638,7 @@ LLAudioChannel * LLAudioEngine::getFreeChannel(const F32 priority) void LLAudioEngine::cleanupBuffer(LLAudioBuffer *bufferp) { S32 i; - for (i = 0; i < MAX_BUFFERS; i++) + for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++) { if (mBuffers[i] == bufferp) { @@ -678,7 +656,7 @@ bool LLAudioEngine::preloadSound(const LLUUID &uuid) getAudioData(uuid); // We don't care about the return value, this is just to make sure // that we have an entry, which will mean that the audio engine knows about this - if (gAudioDecodeMgrp->addDecodeRequest(uuid)) + if (LLAudioDecodeMgr::getInstance()->addDecodeRequest(uuid)) { // This means that we do have a local copy, and we're working on decoding it. return true; @@ -953,6 +931,7 @@ LLAudioSource * LLAudioEngine::findAudioSource(const LLUUID &source_id) LLAudioData * LLAudioEngine::getAudioData(const LLUUID &audio_uuid) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_MEDIA; data_map::iterator iter; iter = mAllData.find(audio_uuid); if (iter == mAllData.end()) @@ -1039,7 +1018,7 @@ void LLAudioEngine::startNextTransfer() // Check all channels for currently playing sounds. F32 max_pri = -1.f; - for (i = 0; i < MAX_CHANNELS; i++) + for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++) { if (!mChannels[i]) { @@ -1067,7 +1046,7 @@ void LLAudioEngine::startNextTransfer() continue; } - if (!adp->hasLocalData() && adp->hasValidData()) + if (!adp->hasLocalData() && !adp->hasDecodeFailed()) { asset_id = adp->getID(); max_pri = asp->getPriority(); @@ -1078,7 +1057,7 @@ void LLAudioEngine::startNextTransfer() if (asset_id.isNull()) { max_pri = -1.f; - for (i = 0; i < MAX_CHANNELS; i++) + for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++) { if (!mChannels[i]) { @@ -1103,7 +1082,7 @@ void LLAudioEngine::startNextTransfer() continue; } - if (!adp->hasLocalData() && adp->hasValidData()) + if (!adp->hasLocalData() && !adp->hasDecodeFailed()) { asset_id = adp->getID(); max_pri = asp->getPriority(); @@ -1115,7 +1094,7 @@ void LLAudioEngine::startNextTransfer() if (asset_id.isNull()) { max_pri = -1.f; - for (i = 0; i < MAX_CHANNELS; i++) + for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++) { if (!mChannels[i]) { @@ -1143,7 +1122,7 @@ void LLAudioEngine::startNextTransfer() continue; } - if (!adp->hasLocalData() && adp->hasValidData()) + if (!adp->hasLocalData() && !adp->hasDecodeFailed()) { asset_id = adp->getID(); max_pri = asp->getPriority(); @@ -1171,7 +1150,7 @@ void LLAudioEngine::startNextTransfer() } adp = asp->getCurrentData(); - if (adp && !adp->hasLocalData() && adp->hasValidData()) + if (adp && !adp->hasLocalData() && !adp->hasDecodeFailed()) { asset_id = adp->getID(); max_pri = asp->getPriority(); @@ -1179,7 +1158,7 @@ void LLAudioEngine::startNextTransfer() } adp = asp->getQueuedData(); - if (adp && !adp->hasLocalData() && adp->hasValidData()) + if (adp && !adp->hasLocalData() && !adp->hasDecodeFailed()) { asset_id = adp->getID(); max_pri = asp->getPriority(); @@ -1194,7 +1173,7 @@ void LLAudioEngine::startNextTransfer() continue; } - if (!adp->hasLocalData() && adp->hasValidData()) + if (!adp->hasLocalData() && !adp->hasDecodeFailed()) { asset_id = adp->getID(); max_pri = asp->getPriority(); @@ -1235,7 +1214,7 @@ void LLAudioEngine::assetCallback(const LLUUID &uuid, LLAssetType::EType type, v LLAudioData *adp = gAudiop->getAudioData(uuid); if (adp) { // Make sure everything is cleared - adp->setHasValidData(false); + adp->setHasDecodeFailed(true); adp->setHasLocalData(false); adp->setHasDecodedData(false); adp->setHasCompletedDecode(true); @@ -1252,9 +1231,9 @@ void LLAudioEngine::assetCallback(const LLUUID &uuid, LLAssetType::EType type, v else { // LL_INFOS() << "Got asset callback with good audio data for " << uuid << ", making decode request" << LL_ENDL; - adp->setHasValidData(true); + adp->setHasDecodeFailed(false); adp->setHasLocalData(true); - gAudioDecodeMgrp->addDecodeRequest(uuid); + LLAudioDecodeMgr::getInstance()->addDecodeRequest(uuid); } } gAudiop->mCurrentTransfer = LLUUID::null; @@ -1324,11 +1303,15 @@ void LLAudioSource::update() { // Hack - try and load the sound. Will do this as a callback // on decode later. - if (adp->load() && adp->getBuffer()) + if (adp->getBuffer()) { play(adp->getID()); } - else if (adp->hasCompletedDecode()) // Only mark corrupted after decode is done + else if (adp->hasDecodedData() && !adp->hasWAVLoadFailed()) + { + adp->load(); + } + else if (adp->hasCompletedDecode() && adp->hasDecodeFailed()) // Only mark corrupted after decode is done { LL_WARNS() << "Marking LLAudioSource corrupted for " << adp->getID() << LL_ENDL; mCorrupted = true ; @@ -1624,12 +1607,12 @@ bool LLAudioSource::hasPendingPreloads() const { LLAudioData *adp = iter->second; // note: a bad UUID will forever be !hasDecodedData() - // but also !hasValidData(), hence the check for hasValidData() + // but also hasDecodeFailed(), hence the check for hasDecodeFailed() if (!adp) { continue; } - if (!adp->hasDecodedData() && adp->hasValidData()) + if (!adp->hasDecodedData() && !adp->hasDecodeFailed()) { // This source is still waiting for a preload return true; @@ -1786,7 +1769,8 @@ LLAudioData::LLAudioData(const LLUUID &uuid) : mHasLocalData(false), mHasDecodedData(false), mHasCompletedDecode(false), - mHasValidData(true) + mHasDecodeFailed(false), + mHasWAVLoadFailed(false) { if (uuid.isNull()) { @@ -1821,12 +1805,14 @@ bool LLAudioData::load() { // We already have this sound in a buffer, don't do anything. LL_INFOS() << "Already have a buffer for this sound, don't bother loading!" << LL_ENDL; + mHasWAVLoadFailed = false; return true; } if (!gAudiop) { LL_WARNS("AudioEngine") << "LLAudioEngine instance doesn't exist!" << LL_ENDL; + mHasWAVLoadFailed = true; return false; } @@ -1835,6 +1821,8 @@ bool LLAudioData::load() { // No free buffers, abort. LL_INFOS() << "Not able to allocate a new audio buffer, aborting." << LL_ENDL; + // *TODO: Mark this failure differently so the audio engine could retry loading this buffer in the future + mHasWAVLoadFailed = true; return true; } @@ -1843,7 +1831,8 @@ bool LLAudioData::load() mID.toString(uuid_str); wav_path= gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str) + ".dsf"; - if (!mBufferp->loadWAV(wav_path)) + mHasWAVLoadFailed = !mBufferp->loadWAV(wav_path); + if (mHasWAVLoadFailed) { // Hrm. Right now, let's unset the buffer, since it's empty. gAudiop->cleanupBuffer(mBufferp); diff --git a/indra/llaudio/llaudioengine.h b/indra/llaudio/llaudioengine.h old mode 100644 new mode 100755 index b5fd4c27a1..65356ba00e --- a/indra/llaudio/llaudioengine.h +++ b/indra/llaudio/llaudioengine.h @@ -47,8 +47,8 @@ const F32 LL_WIND_UNDERWATER_CENTER_FREQ = 20.f; const F32 ATTACHED_OBJECT_TIMEOUT = 5.0f; const F32 DEFAULT_MIN_DISTANCE = 2.0f; -#define MAX_CHANNELS 30 -#define MAX_BUFFERS 40 // Some extra for preloading, maybe? +#define LL_MAX_AUDIO_CHANNELS 30 +#define LL_MAX_AUDIO_BUFFERS 40 // Some extra for preloading, maybe? class LLAudioSource; class LLAudioData; @@ -88,7 +88,7 @@ public: virtual ~LLAudioEngine(); // initialization/startup/shutdown - virtual bool init(const S32 num_channels, void *userdata, const std::string &app_title); + virtual bool init(void *userdata, const std::string &app_title); virtual std::string getDriverName(bool verbose) = 0; virtual void shutdown(); @@ -96,7 +96,7 @@ public: //virtual void processQueue(const LLUUID &sound_guid); virtual void setListener(LLVector3 pos,LLVector3 vel,LLVector3 up,LLVector3 at); virtual void updateWind(LLVector3 direction, F32 camera_height_above_water) = 0; - virtual void idle(F32 max_decode_time = 0.f); + virtual void idle(); virtual void updateChannels(); // @@ -209,7 +209,6 @@ protected: S32 mLastStatus; - S32 mNumChannels; bool mEnableWind; LLUUID mCurrentTransfer; // Audio file currently being transferred by the system @@ -224,11 +223,11 @@ protected: source_map mAllSources; data_map mAllData; - LLAudioChannel *mChannels[MAX_CHANNELS]; + std::array mChannels; // Buffers needs to change into a different data structure, as the number of buffers // that we have active should be limited by RAM usage, not count. - LLAudioBuffer *mBuffers[MAX_BUFFERS]; + std::array mBuffers; F32 mMasterGain; F32 mInternalGain; // Actual gain set; either mMasterGain or 0 when mMuted is true. @@ -360,32 +359,36 @@ protected: class LLAudioData { -public: - LLAudioData(const LLUUID &uuid); - bool load(); + public: + LLAudioData(const LLUUID &uuid); + bool load(); - LLUUID getID() const { return mID; } - LLAudioBuffer *getBuffer() const { return mBufferp; } + LLUUID getID() const { return mID; } + LLAudioBuffer *getBuffer() const { return mBufferp; } - bool hasLocalData() const { return mHasLocalData; } - bool hasDecodedData() const { return mHasDecodedData; } - bool hasCompletedDecode() const { return mHasCompletedDecode; } - bool hasValidData() const { return mHasValidData; } + bool hasLocalData() const { return mHasLocalData; } + bool hasDecodedData() const { return mHasDecodedData; } + bool hasCompletedDecode() const { return mHasCompletedDecode; } + bool hasDecodeFailed() const { return mHasDecodeFailed; } + bool hasWAVLoadFailed() const { return mHasWAVLoadFailed; } - void setHasLocalData(const bool hld) { mHasLocalData = hld; } - void setHasDecodedData(const bool hdd) { mHasDecodedData = hdd; } - void setHasCompletedDecode(const bool hcd) { mHasCompletedDecode = hcd; } - void setHasValidData(const bool hvd) { mHasValidData = hvd; } + void setHasLocalData(const bool hld) { mHasLocalData = hld; } + void setHasDecodedData(const bool hdd) { mHasDecodedData = hdd; } + void setHasCompletedDecode(const bool hcd) { mHasCompletedDecode = hcd; } + void setHasDecodeFailed(const bool hdf) { mHasDecodeFailed = hdf; } + void setHasWAVLoadFailed(const bool hwlf) { mHasWAVLoadFailed = hwlf; } - friend class LLAudioEngine; // Severe laziness, bad. + friend class LLAudioEngine; // Severe laziness, bad. -protected: - LLUUID mID; - LLAudioBuffer *mBufferp; // If this data is being used by the audio system, a pointer to the buffer will be set here. - bool mHasLocalData; // Set true if the sound asset file is available locally - bool mHasDecodedData; // Set true if the sound file has been decoded - bool mHasCompletedDecode; // Set true when the sound is decoded - bool mHasValidData; // Set false if decoding failed, meaning the sound asset is bad + protected: + LLUUID mID; + LLAudioBuffer *mBufferp; // If this data is being used by the audio system, a pointer to the buffer will be set here. + bool mHasLocalData; // Set true if the encoded sound asset file is available locally + bool mHasDecodedData; // Set true if the decoded sound file is available on disk + bool mHasCompletedDecode; // Set true when the sound is decoded + bool mHasDecodeFailed; // Set true if decoding failed, meaning the sound asset is bad + bool mHasWAVLoadFailed; // Set true if loading the decoded WAV file failed, meaning the sound asset should be decoded instead if + // possible }; diff --git a/indra/llaudio/llaudioengine_fmodstudio.cpp b/indra/llaudio/llaudioengine_fmodstudio.cpp index b0c87b0208..ce2c7d5551 100644 --- a/indra/llaudio/llaudioengine_fmodstudio.cpp +++ b/indra/llaudio/llaudioengine_fmodstudio.cpp @@ -74,7 +74,7 @@ static inline bool Check_FMOD_Error(FMOD_RESULT result, const char *string) return true; } -bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, const std::string &app_title) +bool LLAudioEngine_FMODSTUDIO::init(void* userdata, const std::string &app_title) { U32 version; FMOD_RESULT result; @@ -86,7 +86,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons return false; //will call LLAudioEngine_FMODSTUDIO::allocateListener, which needs a valid mSystem pointer. - LLAudioEngine::init(num_channels, userdata, app_title); + LLAudioEngine::init(userdata, app_title); result = mSystem->getVersion(&version); Check_FMOD_Error(result, "FMOD::System::getVersion"); @@ -98,7 +98,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons } // In this case, all sounds, PLUS wind and stream will be software. - result = mSystem->setSoftwareChannels(num_channels + 2); + result = mSystem->setSoftwareChannels(LL_MAX_AUDIO_CHANNELS + 2); Check_FMOD_Error(result, "FMOD::System::setSoftwareChannels"); FMOD_ADVANCEDSETTINGS settings; @@ -127,7 +127,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons { LL_DEBUGS("AppInit") << "Trying PulseAudio audio output..." << LL_ENDL; if (mSystem->setOutput(FMOD_OUTPUTTYPE_PULSEAUDIO) == FMOD_OK && - (result = mSystem->init(num_channels + 2, fmod_flags, const_cast(app_title.c_str()))) == FMOD_OK) + (result = mSystem->init(LL_MAX_AUDIO_CHANNELS + 2, fmod_flags, const_cast(app_title.c_str()))) == FMOD_OK) { LL_DEBUGS("AppInit") << "PulseAudio output initialized OKAY" << LL_ENDL; audio_ok = true; @@ -149,7 +149,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons { LL_DEBUGS("AppInit") << "Trying ALSA audio output..." << LL_ENDL; if (mSystem->setOutput(FMOD_OUTPUTTYPE_ALSA) == FMOD_OK && - (result = mSystem->init(num_channels + 2, fmod_flags, 0)) == FMOD_OK) + (result = mSystem->init(LL_MAX_AUDIO_CHANNELS + 2, fmod_flags, 0)) == FMOD_OK) { LL_DEBUGS("AppInit") << "ALSA audio output initialized OKAY" << LL_ENDL; audio_ok = true; @@ -190,7 +190,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons // initialize the FMOD engine // number of channel in this case looks to be identiacal to number of max simultaneously // playing objects and we can set practically any number - result = mSystem->init(num_channels + 2, fmod_flags, 0); + result = mSystem->init(LL_MAX_AUDIO_CHANNELS + 2, fmod_flags, 0); if (Check_FMOD_Error(result, "Error initializing FMOD Studio with default settins, retrying with other format")) { result = mSystem->setSoftwareFormat(44100, FMOD_SPEAKERMODE_STEREO, 0/*- ignore*/); @@ -198,7 +198,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons { return false; } - result = mSystem->init(num_channels + 2, fmod_flags, 0); + result = mSystem->init(LL_MAX_AUDIO_CHANNELS + 2, fmod_flags, 0); } if (Check_FMOD_Error(result, "Error initializing FMOD Studio")) { diff --git a/indra/llaudio/llaudioengine_fmodstudio.h b/indra/llaudio/llaudioengine_fmodstudio.h index f2361df1b6..d3d6d69685 100644 --- a/indra/llaudio/llaudioengine_fmodstudio.h +++ b/indra/llaudio/llaudioengine_fmodstudio.h @@ -51,7 +51,7 @@ public: virtual ~LLAudioEngine_FMODSTUDIO(); // initialization/startup/shutdown - virtual bool init(const S32 num_channels, void *user_data, const std::string &app_title); + virtual bool init(void *user_data, const std::string &app_title); virtual std::string getDriverName(bool verbose); virtual void allocateListener(); diff --git a/indra/llaudio/llaudioengine_openal.cpp b/indra/llaudio/llaudioengine_openal.cpp index 3bdd0302ee..a87b4c07e2 100644 --- a/indra/llaudio/llaudioengine_openal.cpp +++ b/indra/llaudio/llaudioengine_openal.cpp @@ -52,10 +52,10 @@ LLAudioEngine_OpenAL::~LLAudioEngine_OpenAL() } // virtual -bool LLAudioEngine_OpenAL::init(const S32 num_channels, void* userdata, const std::string &app_title) +bool LLAudioEngine_OpenAL::init(void* userdata, const std::string &app_title) { mWindGen = NULL; - LLAudioEngine::init(num_channels, userdata, app_title); + LLAudioEngine::init(userdata, app_title); if(!alutInit(NULL, NULL)) { diff --git a/indra/llcommon/workqueue.h b/indra/llcommon/workqueue.h index 96574a18b9..70fd65bd0c 100644 --- a/indra/llcommon/workqueue.h +++ b/indra/llcommon/workqueue.h @@ -403,7 +403,7 @@ namespace LL [result = std::forward(callable)(), callback = std::move(callback)] () - { callback(std::move(result)); }; + mutable { callback(std::move(result)); }; } }; @@ -449,7 +449,7 @@ namespace LL callable = std::move(callable), callback = std::move(callback)] () - { + mutable { // Use postMaybe() below in case this originating WorkQueue // has been closed or destroyed. Remember, the outer lambda is // now running on a thread servicing the target WorkQueue, and @@ -513,7 +513,7 @@ namespace LL // We dare to bind a reference to Promise because it's // specifically designed for cross-thread communication. [&promise, callable = std::move(callable)]() - { + mutable { try { // call the caller's callable and trigger promise with result @@ -542,7 +542,7 @@ namespace LL time, // &promise is designed for cross-thread access [&promise, callable = std::move(callable)]() - { + mutable { try { callable(); diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 979a888814..b5ed8d8647 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -5020,8 +5020,7 @@ void LLAppViewer::idle() audio_update_wind(false); // this line actually commits the changes we've made to source positions, etc. - const F32 max_audio_decode_time = 0.002f; // 2 ms decode time - gAudiop->idle(max_audio_decode_time); + gAudiop->idle(); } } diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 98b2bc703b..145aba4a71 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -661,7 +661,7 @@ bool idle_startup() #else void* window_handle = NULL; #endif - bool init = gAudiop->init(kAUDIO_NUM_SOURCES, window_handle, LLAppViewer::instance()->getSecondLifeTitle()); + bool init = gAudiop->init(window_handle, LLAppViewer::instance()->getSecondLifeTitle()); if(init) { gAudiop->setMuted(TRUE); diff --git a/indra/newview/llvieweraudio.h b/indra/newview/llvieweraudio.h index 782285ce36..febae36ae8 100644 --- a/indra/newview/llvieweraudio.h +++ b/indra/newview/llvieweraudio.h @@ -33,8 +33,6 @@ // comment out to turn off wind #define kAUDIO_ENABLE_WIND //#define kAUDIO_ENABLE_WATER 1 // comment out to turn off water -#define kAUDIO_NUM_BUFFERS 30 -#define kAUDIO_NUM_SOURCES 30 void init_audio(); void audio_update_volume(bool force_update = true); From 30252bceb068bc1b687b18a85ab5e1b5a98c1184 Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Tue, 31 May 2022 12:49:53 -0700 Subject: [PATCH 12/13] SL-16182: LLAudioDecodeMgr: Get general worker thread count directly from ThreadPool --- indra/llaudio/llaudiodecodemgr.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/indra/llaudio/llaudiodecodemgr.cpp b/indra/llaudio/llaudiodecodemgr.cpp index cc5abee08a..4c4f4d3d0e 100755 --- a/indra/llaudio/llaudiodecodemgr.cpp +++ b/indra/llaudio/llaudiodecodemgr.cpp @@ -35,6 +35,7 @@ #include "llendianswizzle.h" #include "llassetstorage.h" #include "llrefcount.h" +#include "threadpool.h" #include "workqueue.h" #include "llvorbisencode.h" @@ -575,8 +576,10 @@ void LLAudioDecodeMgr::Impl::startMoreDecodes() // *NOTE: main_queue->postTo casts this refcounted smart pointer to a weak // pointer LL::WorkQueue::ptr_t general_queue = LL::WorkQueue::getInstance("General"); + const LL::ThreadPool::ptr_t general_thread_pool = LL::ThreadPool::getInstance("General"); llassert_always(main_queue); llassert_always(general_queue); + llassert_always(general_thread_pool); // Set max decodes to double the thread count of the general work queue. // This ensures the general work queue is full, but prevents theoretical // buildup of buffers in memory due to disk writes once the @@ -585,9 +588,9 @@ void LLAudioDecodeMgr::Impl::startMoreDecodes() // without modifying/removing LLVorbisDecodeState, at which point we should // consider decoding the audio during the asset download process. // -Cosmic,2022-05-11 - const size_t MAX_DECODES = 4 * 2; + const size_t max_decodes = general_thread_pool->getWidth() * 2; - while (!mDecodeQueue.empty() && mDecodes.size() < MAX_DECODES) + while (!mDecodeQueue.empty() && mDecodes.size() < max_decodes) { const LLUUID decode_id = mDecodeQueue.front(); mDecodeQueue.pop_front(); From 462b1c6544425c0a37f27da32fbf770e9e7373f9 Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Tue, 14 Jun 2022 13:00:53 -0700 Subject: [PATCH 13/13] SL-16182: Fix Mac compiler errors --- indra/llaudio/llaudiodecodemgr.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/llaudio/llaudiodecodemgr.cpp b/indra/llaudio/llaudiodecodemgr.cpp index 4c4f4d3d0e..bc082bf39a 100755 --- a/indra/llaudio/llaudiodecodemgr.cpp +++ b/indra/llaudio/llaudiodecodemgr.cpp @@ -533,7 +533,7 @@ void LLVorbisDecodeState::flushBadFile() class LLAudioDecodeMgr::Impl { friend class LLAudioDecodeMgr; - LLAudioDecodeMgr::Impl(); + Impl(); public: void processQueue(); @@ -609,7 +609,7 @@ void LLAudioDecodeMgr::Impl::startMoreDecodes() mDecodes[decode_id] = LLPointer(NULL); main_queue->postTo( general_queue, - [decode_id, this]() // Work done on general queue + [decode_id]() // Work done on general queue { LLPointer decode_state = beginDecodingAndWritingAudio(decode_id);