summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorErik Kundiman <erik@megapahit.org>2025-03-06 09:36:23 +0800
committerErik Kundiman <erik@megapahit.org>2025-03-06 09:36:23 +0800
commitd13a43c74f9c7e450f87edab0bcd9db39ee7c56e (patch)
treed40e4e3c1f5ce0cf80b11b2d03568c48cfc5e88f
parent5bf93b728ca7c671b6b812e753a40dcdb55b5d0d (diff)
parentaa060ae56c62869bb1d0f590e9a6eb681aabcefc (diff)
Merge remote-tracking branch 'secondlife/release/2025.03' into 2025.03
-rw-r--r--.github/workflows/cla.yaml2
-rw-r--r--indra/llappearance/llavatarappearance.cpp1
-rw-r--r--indra/llcommon/llapp.h2
-rw-r--r--indra/llcommon/llcoros.cpp58
-rw-r--r--indra/llinventory/llsettingsbase.cpp11
-rw-r--r--indra/llinventory/llsettingsbase.h5
-rw-r--r--indra/llmessage/llcoproceduremanager.cpp7
-rw-r--r--indra/llmessage/llcorehttputil.cpp10
-rw-r--r--indra/llmessage/llpacketbuffer.cpp26
-rw-r--r--indra/llmessage/llpacketbuffer.h12
-rw-r--r--indra/llmessage/llpacketring.cpp458
-rw-r--r--indra/llmessage/llpacketring.h86
-rw-r--r--indra/llmessage/message.cpp14
-rw-r--r--indra/llmessage/message.h3
-rw-r--r--indra/llmessage/net.cpp10
-rw-r--r--indra/llrender/llfontbitmapcache.cpp2
-rw-r--r--indra/llrender/llfontbitmapcache.h2
-rw-r--r--indra/llrender/llfontfreetype.cpp2
-rw-r--r--indra/llrender/llfontfreetype.h2
-rw-r--r--indra/llrender/llfontgl.cpp5
-rw-r--r--indra/llrender/llfontgl.h2
-rw-r--r--indra/llrender/llfontvertexbuffer.cpp6
-rw-r--r--indra/llrender/llfontvertexbuffer.h2
-rw-r--r--indra/llrender/llgl.cpp15
-rw-r--r--indra/llui/llfolderview.cpp2
-rw-r--r--indra/llxml/llxmlnode.cpp1
-rw-r--r--indra/newview/VIEWER_VERSION.txt2
-rw-r--r--indra/newview/app_settings/settings.xml13
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl11
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/postDeferredF.glsl3
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/postDeferredGammaCorrect.glsl5
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/postDeferredNoDoFF.glsl2
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/postDeferredTonemap.glsl3
-rw-r--r--indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl4
-rw-r--r--indra/newview/app_settings/shaders/class3/environment/waterF.glsl11
-rw-r--r--indra/newview/gltfscenemanager.cpp19
-rw-r--r--indra/newview/llagent.cpp5
-rw-r--r--indra/newview/llappdelegate-objc.mm3
-rw-r--r--indra/newview/llappviewer.cpp103
-rw-r--r--indra/newview/llappviewer.h8
-rw-r--r--indra/newview/llappviewermacosx-for-objc.h1
-rw-r--r--indra/newview/llappviewermacosx.cpp27
-rw-r--r--indra/newview/llappviewerwin32.cpp4
-rw-r--r--indra/newview/llappviewerwin32.h2
-rw-r--r--indra/newview/llavatarpropertiesprocessor.cpp1
-rw-r--r--indra/newview/lldrawpoolwater.cpp2
-rw-r--r--indra/newview/llfloaterlagmeter.cpp1
-rw-r--r--indra/newview/llgiveinventory.cpp12
-rw-r--r--indra/newview/llheroprobemanager.cpp11
-rw-r--r--indra/newview/llheroprobemanager.h3
-rw-r--r--indra/newview/llmeshrepository.cpp1205
-rw-r--r--indra/newview/llmeshrepository.h211
-rw-r--r--indra/newview/llpanelface.cpp83
-rw-r--r--indra/newview/llpanelface.h5
-rw-r--r--indra/newview/llpanelprimmediacontrols.cpp2
-rw-r--r--indra/newview/llpanelprofilepicks.cpp77
-rw-r--r--indra/newview/llpanelprofilepicks.h7
-rw-r--r--indra/newview/llscrollingpanelparam.h1
-rw-r--r--indra/newview/llselectmgr.cpp8
-rw-r--r--indra/newview/llstartup.cpp287
-rw-r--r--indra/newview/lltextureview.cpp2
-rw-r--r--indra/newview/lltoolbrush.cpp5
-rw-r--r--indra/newview/lltooldraganddrop.cpp23
-rw-r--r--indra/newview/lltoolplacer.cpp5
-rw-r--r--indra/newview/lltoolselectland.cpp5
-rw-r--r--indra/newview/llviewermenu.cpp38
-rw-r--r--indra/newview/llviewermessage.cpp35
-rw-r--r--indra/newview/llviewerobject.cpp12
-rw-r--r--indra/newview/llviewerobject.h1
-rw-r--r--indra/newview/llviewertexture.cpp8
-rw-r--r--indra/newview/llviewertexturelist.cpp21
-rw-r--r--indra/newview/llviewerwindow.cpp2
-rw-r--r--indra/newview/llviewerwindow.h1
-rw-r--r--indra/newview/llvoavatar.cpp135
-rw-r--r--indra/newview/llvoavatar.h1
-rw-r--r--indra/newview/llvosky.cpp12
-rw-r--r--indra/newview/llworld.cpp1
-rw-r--r--indra/newview/pipeline.cpp15
-rw-r--r--indra/newview/skins/default/xui/da/floater_avatar_textures.xml2
-rw-r--r--indra/newview/skins/default/xui/de/floater_avatar_textures.xml2
-rw-r--r--indra/newview/skins/default/xui/en/floater_avatar_textures.xml8
-rw-r--r--indra/newview/skins/default/xui/en/menu_viewer.xml14
-rw-r--r--indra/newview/skins/default/xui/en/notifications.xml12
-rw-r--r--indra/newview/skins/default/xui/en/panel_tools_texture.xml8
-rw-r--r--indra/newview/skins/default/xui/es/floater_avatar_textures.xml2
-rw-r--r--indra/newview/skins/default/xui/fr/floater_avatar_textures.xml2
-rw-r--r--indra/newview/skins/default/xui/it/floater_avatar_textures.xml2
-rw-r--r--indra/newview/skins/default/xui/ja/floater_avatar_textures.xml2
-rw-r--r--indra/newview/skins/default/xui/pl/floater_avatar_textures.xml2
-rw-r--r--indra/newview/skins/default/xui/pt/floater_avatar_textures.xml2
-rw-r--r--indra/newview/skins/default/xui/ru/floater_avatar_textures.xml2
-rw-r--r--indra/newview/skins/default/xui/tr/floater_avatar_textures.xml2
-rw-r--r--indra/newview/skins/default/xui/zh/floater_avatar_textures.xml2
93 files changed, 2248 insertions, 1026 deletions
diff --git a/.github/workflows/cla.yaml b/.github/workflows/cla.yaml
index 013adc9ca8..7866f943b5 100644
--- a/.github/workflows/cla.yaml
+++ b/.github/workflows/cla.yaml
@@ -23,4 +23,4 @@ jobs:
path-to-signatures: signatures.json
remote-organization-name: secondlife
remote-repository-name: cla-signatures
- allowlist: callum@mbp.localdomain
+ allowlist: callum@mbp.localdomain,rye@lindenlab.com
diff --git a/indra/llappearance/llavatarappearance.cpp b/indra/llappearance/llavatarappearance.cpp
index 95d55c835f..3d66809ed6 100644
--- a/indra/llappearance/llavatarappearance.cpp
+++ b/indra/llappearance/llavatarappearance.cpp
@@ -799,7 +799,6 @@ void LLAvatarAppearance::buildCharacter()
bool status = loadAvatar();
stop_glerror();
-// gPrintMessagesThisFrame = true;
LL_DEBUGS() << "Avatar load took " << timer.getElapsedTimeF32() << " seconds." << LL_ENDL;
if (!status)
diff --git a/indra/llcommon/llapp.h b/indra/llcommon/llapp.h
index 3d18864b80..57f5a112d9 100644
--- a/indra/llcommon/llapp.h
+++ b/indra/llcommon/llapp.h
@@ -282,7 +282,7 @@ public:
LLRunner& getRunner() { return mRunner; }
#ifdef LL_WINDOWS
- virtual void reportCrashToBugsplat(void* pExcepInfo /*EXCEPTION_POINTERS*/) { }
+ virtual bool reportCrashToBugsplat(void* pExcepInfo /*EXCEPTION_POINTERS*/) { return false; }
#endif
public:
diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp
index 3fe7e09e7e..ae0d6e694d 100644
--- a/indra/llcommon/llcoros.cpp
+++ b/indra/llcommon/llcoros.cpp
@@ -303,6 +303,62 @@ std::string LLCoros::launch(const std::string& prefix, const callable_t& callabl
return name;
}
+namespace
+{
+
+#if LL_WINDOWS
+
+static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific
+
+U32 exception_filter(U32 code, struct _EXCEPTION_POINTERS* exception_infop)
+{
+ if (LLApp::instance()->reportCrashToBugsplat((void*)exception_infop))
+ {
+ // Handled
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+ else if (code == STATUS_MSC_EXCEPTION)
+ {
+ // C++ exception, go on
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+ else
+ {
+ // handle it, convert to std::exception
+ return EXCEPTION_EXECUTE_HANDLER;
+ }
+
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+void sehandle(const LLCoros::callable_t& callable)
+{
+ __try
+ {
+ callable();
+ }
+ __except (exception_filter(GetExceptionCode(), GetExceptionInformation()))
+ {
+ // convert to C++ styled exception
+ // Note: it might be better to use _se_set_translator
+ // if you want exception to inherit full callstack
+ char integer_string[512];
+ sprintf(integer_string, "SEH, code: %lu\n", GetExceptionCode());
+ throw std::exception(integer_string);
+ }
+}
+
+#else // ! LL_WINDOWS
+
+inline void sehandle(const LLCoros::callable_t& callable)
+{
+ callable();
+}
+
+#endif // ! LL_WINDOWS
+
+} // anonymous namespace
+
// Top-level wrapper around caller's coroutine callable.
// Normally we like to pass strings and such by const reference -- but in this
// case, we WANT to copy both the name and the callable to our local stack!
@@ -330,6 +386,7 @@ void LLCoros::toplevel(std::string name, callable_t callable)
// viewer will carry on.
LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << name));
}
+#ifndef LL_WINDOWS
catch (...)
{
// Stash any OTHER kind of uncaught exception in the rethrow() queue
@@ -338,6 +395,7 @@ void LLCoros::toplevel(std::string name, callable_t callable)
<< name << LL_ENDL;
LLCoros::instance().saveException(name, std::current_exception());
}
+#endif // ! LL_WINDOWS
}
//static
diff --git a/indra/llinventory/llsettingsbase.cpp b/indra/llinventory/llsettingsbase.cpp
index 41162963f2..97979d64e3 100644
--- a/indra/llinventory/llsettingsbase.cpp
+++ b/indra/llinventory/llsettingsbase.cpp
@@ -130,17 +130,6 @@ void LLSettingsBase::saveValuesIfNeeded()
}
//=========================================================================
-void LLSettingsBase::lerpSettings(LLSettingsBase &other, F64 mix)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_ENVIRONMENT;
- saveValuesIfNeeded();
- stringset_t skip = getSkipInterpolateKeys();
- stringset_t slerps = getSlerpKeys();
- mSettings = interpolateSDMap(mSettings, other.getSettings(), other.getParameterMap(), mix, skip, slerps);
- setDirtyFlag(true);
- loadValuesFromLLSD();
-}
-
void LLSettingsBase::lerpVector2(LLVector2& a, const LLVector2& b, F32 mix)
{
a.mV[0] = lerp(a.mV[0], b.mV[0], mix);
diff --git a/indra/llinventory/llsettingsbase.h b/indra/llinventory/llsettingsbase.h
index 816ff3e111..7de71588ef 100644
--- a/indra/llinventory/llsettingsbase.h
+++ b/indra/llinventory/llsettingsbase.h
@@ -348,13 +348,8 @@ protected:
LLSettingsBase();
LLSettingsBase(const LLSD setting);
- static LLSD settingValidation(LLSD settings);
-
typedef std::set<std::string> stringset_t;
- // combining settings objects. Customize for specific setting types
- virtual void lerpSettings(LLSettingsBase &other, BlendFactor mix);
-
// combining settings maps where it can based on mix rate
// @settings initial value (mix==0)
// @other target value (mix==1)
diff --git a/indra/llmessage/llcoproceduremanager.cpp b/indra/llmessage/llcoproceduremanager.cpp
index 263670bdac..6a663a8e97 100644
--- a/indra/llmessage/llcoproceduremanager.cpp
+++ b/indra/llmessage/llcoproceduremanager.cpp
@@ -301,12 +301,12 @@ LLCoprocedurePool::LLCoprocedurePool(const std::string &poolName, size_t size):
mPoolSize(size),
mActiveCoprocsCount(0),
mPending(0),
- mPendingCoprocs(std::make_shared<CoprocQueue_t>(LLCoprocedureManager::DEFAULT_QUEUE_SIZE)),
mHTTPPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID),
mCoroMapping()
{
try
{
+ mPendingCoprocs = std::make_shared<CoprocQueue_t>(LLCoprocedureManager::DEFAULT_QUEUE_SIZE);
// store in our LLTempBoundListener so that when the LLCoprocedurePool is
// destroyed, we implicitly disconnect from this LLEventPump
// Monitores application status
@@ -339,6 +339,11 @@ LLCoprocedurePool::LLCoprocedurePool(const std::string &poolName, size_t size):
llassert(0); // Fix Me! Ignoring missing listener!
}
+ catch (std::bad_alloc&)
+ {
+ LLError::LLUserWarningMsg::showOutOfMemory();
+ LL_ERRS("CoProcMgr") << "Bad memory allocation in LLCoprocedurePool::LLCoprocedurePool!" << LL_ENDL;
+ }
for (size_t count = 0; count < mPoolSize; ++count)
{
diff --git a/indra/llmessage/llcorehttputil.cpp b/indra/llmessage/llcorehttputil.cpp
index 918a69be6f..992e145758 100644
--- a/indra/llmessage/llcorehttputil.cpp
+++ b/indra/llmessage/llcorehttputil.cpp
@@ -295,7 +295,15 @@ void HttpCoroHandler::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespons
}
else
{
- result = this->handleSuccess(response, status);
+ try
+ {
+ result = this->handleSuccess(response, status);
+ }
+ catch (std::bad_alloc&)
+ {
+ LLError::LLUserWarningMsg::showOutOfMemory();
+ LL_ERRS("CoreHTTP") << "Failed to allocate memory for response handling." << LL_ENDL;
+ }
}
buildStatusEntry(response, status, result);
diff --git a/indra/llmessage/llpacketbuffer.cpp b/indra/llmessage/llpacketbuffer.cpp
index dc5c7a73cb..0b04a560be 100644
--- a/indra/llmessage/llpacketbuffer.cpp
+++ b/indra/llmessage/llpacketbuffer.cpp
@@ -32,8 +32,6 @@
#include "lltimer.h"
#include "llhost.h"
-///////////////////////////////////////////////////////////
-
LLPacketBuffer::LLPacketBuffer(const LLHost &host, const char *datap, const S32 size) : mHost(host)
{
mSize = 0;
@@ -41,7 +39,7 @@ LLPacketBuffer::LLPacketBuffer(const LLHost &host, const char *datap, const S32
if (size > NET_BUFFER_SIZE)
{
- LL_ERRS() << "Sending packet > " << NET_BUFFER_SIZE << " of size " << size << LL_ENDL;
+ LL_ERRS() << "Constructing packet with size=" << size << " > " << NET_BUFFER_SIZE << LL_ENDL;
}
else
{
@@ -51,7 +49,6 @@ LLPacketBuffer::LLPacketBuffer(const LLHost &host, const char *datap, const S32
mSize = size;
}
}
-
}
LLPacketBuffer::LLPacketBuffer (S32 hSocket)
@@ -59,18 +56,29 @@ LLPacketBuffer::LLPacketBuffer (S32 hSocket)
init(hSocket);
}
-///////////////////////////////////////////////////////////
-
LLPacketBuffer::~LLPacketBuffer ()
{
}
-///////////////////////////////////////////////////////////
-
-void LLPacketBuffer::init (S32 hSocket)
+void LLPacketBuffer::init(S32 hSocket)
{
mSize = receive_packet(hSocket, mData);
mHost = ::get_sender();
mReceivingIF = ::get_receiving_interface();
}
+void LLPacketBuffer::init(const char* buffer, S32 data_size, const LLHost& host)
+{
+ if (data_size > NET_BUFFER_SIZE)
+ {
+ LL_ERRS() << "Initializing packet with size=" << data_size << " > " << NET_BUFFER_SIZE << LL_ENDL;
+ }
+ else
+ {
+ memcpy(mData, buffer, data_size);
+ mSize = data_size;
+ mHost = host;
+ mReceivingIF = ::get_receiving_interface();
+ }
+}
+
diff --git a/indra/llmessage/llpacketbuffer.h b/indra/llmessage/llpacketbuffer.h
index a2d2973fb0..ac4012d330 100644
--- a/indra/llmessage/llpacketbuffer.h
+++ b/indra/llmessage/llpacketbuffer.h
@@ -35,20 +35,22 @@ class LLPacketBuffer
{
public:
LLPacketBuffer(const LLHost &host, const char *datap, const S32 size);
- LLPacketBuffer(S32 hSocket); // receive a packet
+ LLPacketBuffer(S32 hSocket); // receive a packet
~LLPacketBuffer();
S32 getSize() const { return mSize; }
const char *getData() const { return mData; }
LLHost getHost() const { return mHost; }
LLHost getReceivingInterface() const { return mReceivingIF; }
+
void init(S32 hSocket);
+ void init(const char* buffer, S32 data_size, const LLHost& host);
protected:
- char mData[NET_BUFFER_SIZE]; // packet data /* Flawfinder : ignore */
- S32 mSize; // size of buffer in bytes
- LLHost mHost; // source/dest IP and port
- LLHost mReceivingIF; // source/dest IP and port
+ char mData[NET_BUFFER_SIZE]; // packet data /* Flawfinder : ignore */
+ S32 mSize; // size of buffer in bytes
+ LLHost mHost; // source/dest IP and port
+ LLHost mReceivingIF; // source/dest IP and port
};
#endif
diff --git a/indra/llmessage/llpacketring.cpp b/indra/llmessage/llpacketring.cpp
index be838770a8..da3c502e9d 100644
--- a/indra/llmessage/llpacketring.cpp
+++ b/indra/llmessage/llpacketring.cpp
@@ -1,6 +1,6 @@
/**
* @file llpacketring.cpp
- * @brief implementation of LLPacketRing class for a packet.
+ * @brief implementation of LLPacketRing class.
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
@@ -43,329 +43,317 @@
#include "message.h"
#include "u64.h"
-///////////////////////////////////////////////////////////
-LLPacketRing::LLPacketRing () :
- mUseInThrottle(false),
- mUseOutThrottle(false),
- mInThrottle(256000.f),
- mOutThrottle(64000.f),
- mActualBitsIn(0),
- mActualBitsOut(0),
- mMaxBufferLength(64000),
- mInBufferLength(0),
- mOutBufferLength(0),
- mDropPercentage(0.0f),
- mPacketsToDrop(0x0)
+constexpr S16 MAX_BUFFER_RING_SIZE = 1024;
+constexpr S16 DEFAULT_BUFFER_RING_SIZE = 256;
+
+LLPacketRing::LLPacketRing ()
+ : mPacketRing(DEFAULT_BUFFER_RING_SIZE, nullptr)
{
+ LLHost invalid_host;
+ for (size_t i = 0; i < mPacketRing.size(); ++i)
+ {
+ mPacketRing[i] = new LLPacketBuffer(invalid_host, nullptr, 0);
+ }
}
-///////////////////////////////////////////////////////////
LLPacketRing::~LLPacketRing ()
{
- cleanup();
+ for (auto packet : mPacketRing)
+ {
+ delete packet;
+ }
+ mPacketRing.clear();
+ mNumBufferedPackets = 0;
+ mNumBufferedBytes = 0;
+ mHeadIndex = 0;
}
-///////////////////////////////////////////////////////////
-void LLPacketRing::cleanup ()
+S32 LLPacketRing::receivePacket (S32 socket, char *datap)
{
- LLPacketBuffer *packetp;
+ bool drop = computeDrop();
+ return (mNumBufferedPackets > 0) ?
+ receiveOrDropBufferedPacket(datap, drop) :
+ receiveOrDropPacket(socket, datap, drop);
+}
- while (!mReceiveQueue.empty())
+bool send_packet_helper(int socket, const char * datap, S32 data_size, LLHost host)
+{
+ if (!LLProxy::isSOCKSProxyEnabled())
{
- packetp = mReceiveQueue.front();
- delete packetp;
- mReceiveQueue.pop();
+ return send_packet(socket, datap, data_size, host.getAddress(), host.getPort());
}
- while (!mSendQueue.empty())
- {
- packetp = mSendQueue.front();
- delete packetp;
- mSendQueue.pop();
- }
-}
+ char headered_send_buffer[NET_BUFFER_SIZE + SOCKS_HEADER_SIZE];
-///////////////////////////////////////////////////////////
-void LLPacketRing::dropPackets (U32 num_to_drop)
-{
- mPacketsToDrop += num_to_drop;
-}
+ proxywrap_t *socks_header = static_cast<proxywrap_t*>(static_cast<void*>(&headered_send_buffer));
+ socks_header->rsv = 0;
+ socks_header->addr = host.getAddress();
+ socks_header->port = htons(host.getPort());
+ socks_header->atype = ADDRESS_IPV4;
+ socks_header->frag = 0;
-///////////////////////////////////////////////////////////
-void LLPacketRing::setDropPercentage (F32 percent_to_drop)
-{
- mDropPercentage = percent_to_drop;
-}
+ memcpy(headered_send_buffer + SOCKS_HEADER_SIZE, datap, data_size);
-void LLPacketRing::setUseInThrottle(const bool use_throttle)
-{
- mUseInThrottle = use_throttle;
+ return send_packet( socket,
+ headered_send_buffer,
+ data_size + SOCKS_HEADER_SIZE,
+ LLProxy::getInstance()->getUDPProxy().getAddress(),
+ LLProxy::getInstance()->getUDPProxy().getPort());
}
-void LLPacketRing::setUseOutThrottle(const bool use_throttle)
+bool LLPacketRing::sendPacket(int socket, const char * datap, S32 data_size, LLHost host)
{
- mUseOutThrottle = use_throttle;
+ mActualBytesOut += data_size;
+ return send_packet_helper(socket, datap, data_size, host);
}
-void LLPacketRing::setInBandwidth(const F32 bps)
+void LLPacketRing::dropPackets (U32 num_to_drop)
{
- mInThrottle.setRate(bps);
+ mPacketsToDrop += num_to_drop;
}
-void LLPacketRing::setOutBandwidth(const F32 bps)
+void LLPacketRing::setDropPercentage (F32 percent_to_drop)
{
- mOutThrottle.setRate(bps);
+ mDropPercentage = percent_to_drop;
}
-///////////////////////////////////////////////////////////
-S32 LLPacketRing::receiveFromRing (S32 socket, char *datap)
-{
- if (mInThrottle.checkOverflow(0))
- {
- // We don't have enough bandwidth, don't give them a packet.
- return 0;
- }
-
- LLPacketBuffer *packetp = NULL;
- if (mReceiveQueue.empty())
+bool LLPacketRing::computeDrop()
+{
+ bool drop= (mDropPercentage > 0.0f && (ll_frand(100.f) < mDropPercentage));
+ if (drop)
{
- // No packets on the queue, don't give them any.
- return 0;
+ ++mPacketsToDrop;
}
-
- S32 packet_size = 0;
- packetp = mReceiveQueue.front();
- mReceiveQueue.pop();
- packet_size = packetp->getSize();
- if (packetp->getData() != NULL)
+ if (mPacketsToDrop > 0)
{
- memcpy(datap, packetp->getData(), packet_size); /*Flawfinder: ignore*/
+ --mPacketsToDrop;
+ drop = true;
}
- // need to set sender IP/port!!
- mLastSender = packetp->getHost();
- mLastReceivingIF = packetp->getReceivingInterface();
- delete packetp;
-
- this->mInBufferLength -= packet_size;
-
- // Adjust the throttle
- mInThrottle.throttleOverflow(packet_size * 8.f);
- return packet_size;
+ return drop;
}
-///////////////////////////////////////////////////////////
-S32 LLPacketRing::receivePacket (S32 socket, char *datap)
+S32 LLPacketRing::receiveOrDropPacket(S32 socket, char *datap, bool drop)
{
S32 packet_size = 0;
- // If using the throttle, simulate a limited size input buffer.
- if (mUseInThrottle)
+ // pull straight from socket
+ if (LLProxy::isSOCKSProxyEnabled())
{
- bool done = false;
-
- // push any current net packet (if any) onto delay ring
- while (!done)
+ char buffer[NET_BUFFER_SIZE + SOCKS_HEADER_SIZE]; /* Flawfinder ignore */
+ packet_size = receive_packet(socket, buffer);
+ if (packet_size > 0)
{
- LLPacketBuffer *packetp;
- packetp = new LLPacketBuffer(socket);
-
- if (packetp->getSize())
- {
- mActualBitsIn += packetp->getSize() * 8;
-
- // Fake packet loss
- if (mDropPercentage && (ll_frand(100.f) < mDropPercentage))
- {
- mPacketsToDrop++;
- }
-
- if (mPacketsToDrop)
- {
- delete packetp;
- packetp = NULL;
- packet_size = 0;
- mPacketsToDrop--;
- }
- }
+ mActualBytesIn += packet_size;
+ }
- // If we faked packet loss, then we don't have a packet
- // to use for buffer overflow testing
- if (packetp)
+ if (packet_size > SOCKS_HEADER_SIZE)
+ {
+ if (drop)
{
- if (mInBufferLength + packetp->getSize() > mMaxBufferLength)
- {
- // Toss it.
- LL_WARNS() << "Throwing away packet, overflowing buffer" << LL_ENDL;
- delete packetp;
- packetp = NULL;
- }
- else if (packetp->getSize())
- {
- mReceiveQueue.push(packetp);
- mInBufferLength += packetp->getSize();
- }
- else
- {
- delete packetp;
- packetp = NULL;
- done = true;
- }
+ packet_size = 0;
}
else
{
- // No packetp, keep going? - no packetp == faked packet loss
- }
- }
-
- // Now, grab data off of the receive queue according to our
- // throttled bandwidth settings.
- packet_size = receiveFromRing(socket, datap);
- }
- else
- {
- // no delay, pull straight from net
- if (LLProxy::isSOCKSProxyEnabled())
- {
- U8 buffer[NET_BUFFER_SIZE + SOCKS_HEADER_SIZE];
- packet_size = receive_packet(socket, static_cast<char*>(static_cast<void*>(buffer)));
-
- if (packet_size > SOCKS_HEADER_SIZE)
- {
// *FIX We are assuming ATYP is 0x01 (IPv4), not 0x03 (hostname) or 0x04 (IPv6)
- memcpy(datap, buffer + SOCKS_HEADER_SIZE, packet_size - SOCKS_HEADER_SIZE);
+ packet_size -= SOCKS_HEADER_SIZE; // The unwrapped packet size
+ memcpy(datap, buffer + SOCKS_HEADER_SIZE, packet_size);
proxywrap_t * header = static_cast<proxywrap_t*>(static_cast<void*>(buffer));
mLastSender.setAddress(header->addr);
mLastSender.setPort(ntohs(header->port));
-
- packet_size -= SOCKS_HEADER_SIZE; // The unwrapped packet size
- }
- else
- {
- packet_size = 0;
+ mLastReceivingIF = ::get_receiving_interface();
}
}
else
{
- packet_size = receive_packet(socket, datap);
- mLastSender = ::get_sender();
+ packet_size = 0;
}
-
- mLastReceivingIF = ::get_receiving_interface();
-
- if (packet_size) // did we actually get a packet?
+ }
+ else
+ {
+ packet_size = receive_packet(socket, datap);
+ if (packet_size > 0)
{
- if (mDropPercentage && (ll_frand(100.f) < mDropPercentage))
+ mActualBytesIn += packet_size;
+ if (drop)
{
- mPacketsToDrop++;
+ packet_size = 0;
}
-
- if (mPacketsToDrop)
+ else
{
- packet_size = 0;
- mPacketsToDrop--;
+ mLastSender = ::get_sender();
+ mLastReceivingIF = ::get_receiving_interface();
}
}
}
-
return packet_size;
}
-bool LLPacketRing::sendPacket(int h_socket, char * send_buffer, S32 buf_size, LLHost host)
+S32 LLPacketRing::receiveOrDropBufferedPacket(char *datap, bool drop)
{
- bool status = true;
- if (!mUseOutThrottle)
+ assert(mNumBufferedPackets > 0);
+ S32 packet_size = 0;
+
+ S16 ring_size = (S16)(mPacketRing.size());
+ S16 packet_index = (mHeadIndex + ring_size - mNumBufferedPackets) % ring_size;
+ LLPacketBuffer* packet = mPacketRing[packet_index];
+ packet_size = packet->getSize();
+ mLastSender = packet->getHost();
+ mLastReceivingIF = packet->getReceivingInterface();
+
+ --mNumBufferedPackets;
+ mNumBufferedBytes -= packet_size;
+ if (mNumBufferedPackets == 0)
{
- return sendPacketImpl(h_socket, send_buffer, buf_size, host );
+ assert(mNumBufferedBytes == 0);
+ }
+
+ if (!drop)
+ {
+ assert(packet_size > 0);
+ memcpy(datap, packet->getData(), packet_size);
}
else
{
- mActualBitsOut += buf_size * 8;
- LLPacketBuffer *packetp = NULL;
- // See if we've got enough throttle to send a packet.
- while (!mOutThrottle.checkOverflow(0.f))
- {
- // While we have enough bandwidth, send a packet from the queue or the current packet
+ packet_size = 0;
+ }
+ return packet_size;
+}
+
+S32 LLPacketRing::bufferInboundPacket(S32 socket)
+{
+ if (mNumBufferedPackets == mPacketRing.size() && mNumBufferedPackets < MAX_BUFFER_RING_SIZE)
+ {
+ expandRing();
+ }
- S32 packet_size = 0;
- if (!mSendQueue.empty())
+ LLPacketBuffer* packet = mPacketRing[mHeadIndex];
+ S32 old_packet_size = packet->getSize();
+ S32 packet_size = 0;
+ if (LLProxy::isSOCKSProxyEnabled())
+ {
+ char buffer[NET_BUFFER_SIZE + SOCKS_HEADER_SIZE]; /* Flawfinder ignore */
+ packet_size = receive_packet(socket, buffer);
+ if (packet_size > 0)
+ {
+ mActualBytesIn += packet_size;
+ if (packet_size > SOCKS_HEADER_SIZE)
{
- // Send a packet off of the queue
- LLPacketBuffer *packetp = mSendQueue.front();
- mSendQueue.pop();
+ // *FIX We are assuming ATYP is 0x01 (IPv4), not 0x03 (hostname) or 0x04 (IPv6)
- mOutBufferLength -= packetp->getSize();
- packet_size = packetp->getSize();
+ proxywrap_t * header = static_cast<proxywrap_t*>(static_cast<void*>(buffer));
+ LLHost sender;
+ sender.setAddress(header->addr);
+ sender.setPort(ntohs(header->port));
- status = sendPacketImpl(h_socket, packetp->getData(), packet_size, packetp->getHost());
+ packet_size -= SOCKS_HEADER_SIZE; // The unwrapped packet size
+ packet->init(buffer + SOCKS_HEADER_SIZE, packet_size, sender);
- delete packetp;
- // Update the throttle
- mOutThrottle.throttleOverflow(packet_size * 8.f);
+ mHeadIndex = (mHeadIndex + 1) % (S16)(mPacketRing.size());
+ if (mNumBufferedPackets < MAX_BUFFER_RING_SIZE)
+ {
+ ++mNumBufferedPackets;
+ mNumBufferedBytes += packet_size;
+ }
+ else
+ {
+ // we overwrote an older packet
+ mNumBufferedBytes += packet_size - old_packet_size;
+ }
}
else
{
- // If the queue's empty, we can just send this packet right away.
- status = sendPacketImpl(h_socket, send_buffer, buf_size, host );
- packet_size = buf_size;
-
- // Update the throttle
- mOutThrottle.throttleOverflow(packet_size * 8.f);
-
- // This was the packet we're sending now, there are no other packets
- // that we need to send
- return status;
+ packet_size = 0;
}
-
- }
-
- // We haven't sent the incoming packet, add it to the queue
- if (mOutBufferLength + buf_size > mMaxBufferLength)
- {
- // Nuke this packet, we overflowed the buffer.
- // Toss it.
- LL_WARNS() << "Throwing away outbound packet, overflowing buffer" << LL_ENDL;
}
- else
+ }
+ else
+ {
+ packet->init(socket);
+ packet_size = packet->getSize();
+ if (packet_size > 0)
{
- static LLTimer queue_timer;
- if ((mOutBufferLength > 4192) && queue_timer.getElapsedTimeF32() > 1.f)
+ mActualBytesIn += packet_size;
+
+ mHeadIndex = (mHeadIndex + 1) % (S16)(mPacketRing.size());
+ if (mNumBufferedPackets < MAX_BUFFER_RING_SIZE)
{
- // Add it to the queue
- LL_INFOS() << "Outbound packet queue " << mOutBufferLength << " bytes" << LL_ENDL;
- queue_timer.reset();
+ ++mNumBufferedPackets;
+ mNumBufferedBytes += packet_size;
+ }
+ else
+ {
+ // we overwrote an older packet
+ mNumBufferedBytes += packet_size - old_packet_size;
}
- packetp = new LLPacketBuffer(host, send_buffer, buf_size);
-
- mOutBufferLength += packetp->getSize();
- mSendQueue.push(packetp);
}
}
-
- return status;
+ return packet_size;
}
-bool LLPacketRing::sendPacketImpl(int h_socket, const char * send_buffer, S32 buf_size, LLHost host)
+S32 LLPacketRing::drainSocket(S32 socket)
{
+ // drain into buffer
+ S32 packet_size = 1;
+ S32 num_loops = 0;
+ S32 old_num_packets = mNumBufferedPackets;
+ while (packet_size > 0)
+ {
+ packet_size = bufferInboundPacket(socket);
+ ++num_loops;
+ }
+ S32 num_dropped_packets = (num_loops - 1 + old_num_packets) - mNumBufferedPackets;
+ if (num_dropped_packets > 0)
+ {
+ // It will eventually be accounted by mDroppedPackets
+ // and mPacketsLost, but track it here for logging purposes.
+ mNumDroppedPackets += num_dropped_packets;
+ }
+ return (S32)(mNumBufferedPackets);
+}
- if (!LLProxy::isSOCKSProxyEnabled())
+bool LLPacketRing::expandRing()
+{
+ // compute larger size
+ constexpr S16 BUFFER_RING_EXPANSION = 256;
+ S16 old_size = (S16)(mPacketRing.size());
+ S16 new_size = llmin(old_size + BUFFER_RING_EXPANSION, MAX_BUFFER_RING_SIZE);
+ if (new_size == old_size)
{
- return send_packet(h_socket, send_buffer, buf_size, host.getAddress(), host.getPort());
+ // mPacketRing is already maxed out
+ return false;
}
- char headered_send_buffer[NET_BUFFER_SIZE + SOCKS_HEADER_SIZE];
+ // make a larger ring and copy packet pointers
+ std::vector<LLPacketBuffer*> new_ring(new_size, nullptr);
+ for (S16 i = 0; i < old_size; ++i)
+ {
+ S16 j = (mHeadIndex + i) % old_size;
+ new_ring[i] = mPacketRing[j];
+ }
- proxywrap_t *socks_header = static_cast<proxywrap_t*>(static_cast<void*>(&headered_send_buffer));
- socks_header->rsv = 0;
- socks_header->addr = host.getAddress();
- socks_header->port = htons(host.getPort());
- socks_header->atype = ADDRESS_IPV4;
- socks_header->frag = 0;
+ // allocate new packets for the remainder of new_ring
+ LLHost invalid_host;
+ for (S16 i = old_size; i < new_size; ++i)
+ {
+ new_ring[i] = new LLPacketBuffer(invalid_host, nullptr, 0);
+ }
- memcpy(headered_send_buffer + SOCKS_HEADER_SIZE, send_buffer, buf_size);
+ // swap the rings and reset mHeadIndex
+ mPacketRing.swap(new_ring);
+ mHeadIndex = mNumBufferedPackets;
+ return true;
+}
- return send_packet( h_socket,
- headered_send_buffer,
- buf_size + SOCKS_HEADER_SIZE,
- LLProxy::getInstance()->getUDPProxy().getAddress(),
- LLProxy::getInstance()->getUDPProxy().getPort());
+void LLPacketRing::dumpPacketRingStats()
+{
+ mNumDroppedPacketsTotal += mNumDroppedPackets;
+ LL_INFOS("Messaging") << "Packet ring stats: " << std::endl
+ << "Buffered packets: " << mNumBufferedPackets << std::endl
+ << "Buffered bytes: " << mNumBufferedBytes << std::endl
+ << "Dropped packets current: " << mNumDroppedPackets << std::endl
+ << "Dropped packets total: " << mNumDroppedPacketsTotal << std::endl
+ << "Dropped packets percentage: " << mDropPercentage << "%" << std::endl
+ << "Actual in bytes: " << mActualBytesIn << std::endl
+ << "Actual out bytes: " << mActualBytesOut << LL_ENDL;
+ mNumDroppedPackets = 0;
}
diff --git a/indra/llmessage/llpacketring.h b/indra/llmessage/llpacketring.h
index f0e95f8524..237efc12e0 100644
--- a/indra/llmessage/llpacketring.h
+++ b/indra/llmessage/llpacketring.h
@@ -25,16 +25,14 @@
* $/LicenseInfo$
*/
-#ifndef LL_LLPACKETRING_H
-#define LL_LLPACKETRING_H
+#pragma once
-#include <queue>
+#include <vector>
#include "llhost.h"
#include "llpacketbuffer.h"
-#include "llproxy.h"
#include "llthrottle.h"
-#include "net.h"
+
class LLPacketRing
{
@@ -42,60 +40,70 @@ public:
LLPacketRing();
~LLPacketRing();
- void cleanup();
+ // receive one packet: either buffered or from the socket
+ S32 receivePacket (S32 socket, char *datap);
+
+ // send one packet
+ bool sendPacket(int h_socket, const char * send_buffer, S32 buf_size, LLHost host);
+
+ // drains packets from socket and returns final mNumBufferedPackets
+ S32 drainSocket(S32 socket);
void dropPackets(U32);
void setDropPercentage (F32 percent_to_drop);
- void setUseInThrottle(const bool use_throttle);
- void setUseOutThrottle(const bool use_throttle);
- void setInBandwidth(const F32 bps);
- void setOutBandwidth(const F32 bps);
- S32 receivePacket (S32 socket, char *datap);
- S32 receiveFromRing (S32 socket, char *datap);
- bool sendPacket(int h_socket, char * send_buffer, S32 buf_size, LLHost host);
+ inline LLHost getLastSender() const;
+ inline LLHost getLastReceivingInterface() const;
- inline LLHost getLastSender();
- inline LLHost getLastReceivingInterface();
+ S32 getActualInBytes() const { return mActualBytesIn; }
+ S32 getActualOutBytes() const { return mActualBytesOut; }
+ S32 getAndResetActualInBits() { S32 bits = mActualBytesIn * 8; mActualBytesIn = 0; return bits;}
+ S32 getAndResetActualOutBits() { S32 bits = mActualBytesOut * 8; mActualBytesOut = 0; return bits;}
- S32 getAndResetActualInBits() { S32 bits = mActualBitsIn; mActualBitsIn = 0; return bits;}
- S32 getAndResetActualOutBits() { S32 bits = mActualBitsOut; mActualBitsOut = 0; return bits;}
-protected:
- bool mUseInThrottle;
- bool mUseOutThrottle;
+ S32 getNumBufferedPackets() const { return (S32)(mNumBufferedPackets); }
+ S32 getNumBufferedBytes() const { return mNumBufferedBytes; }
+ S32 getNumDroppedPackets() const { return mNumDroppedPacketsTotal + mNumDroppedPackets; }
- // For simulating a lower-bandwidth connection - BPS
- LLThrottle mInThrottle;
- LLThrottle mOutThrottle;
+ void dumpPacketRingStats();
+protected:
+ // returns 'true' if we should intentionally drop a packet
+ bool computeDrop();
- S32 mActualBitsIn;
- S32 mActualBitsOut;
- S32 mMaxBufferLength; // How much data can we queue up before dropping data.
- S32 mInBufferLength; // Current incoming buffer length
- S32 mOutBufferLength; // Current outgoing buffer length
+ // returns packet_size of received packet, zero or less if no packet found
+ S32 receiveOrDropPacket(S32 socket, char *datap, bool drop);
+ S32 receiveOrDropBufferedPacket(char *datap, bool drop);
- F32 mDropPercentage; // % of packets to drop
- U32 mPacketsToDrop; // drop next n packets
+ // returns packet_size of packet buffered
+ S32 bufferInboundPacket(S32 socket);
- std::queue<LLPacketBuffer *> mReceiveQueue;
- std::queue<LLPacketBuffer *> mSendQueue;
+ // returns 'true' if ring was expanded
+ bool expandRing();
+protected:
+ std::vector<LLPacketBuffer*> mPacketRing;
+ S16 mHeadIndex { 0 };
+ S16 mNumBufferedPackets { 0 };
+ S32 mNumDroppedPackets { 0 };
+ S32 mNumDroppedPacketsTotal { 0 };
+ S32 mNumBufferedBytes { 0 };
+
+ S32 mActualBytesIn { 0 };
+ S32 mActualBytesOut { 0 };
+ F32 mDropPercentage { 0.0f }; // % of inbound packets to drop
+ U32 mPacketsToDrop { 0 }; // drop next inbound n packets
+
+ // These are the sender and receiving_interface for the last packet delivered by receivePacket()
LLHost mLastSender;
LLHost mLastReceivingIF;
-
-private:
- bool sendPacketImpl(int h_socket, const char * send_buffer, S32 buf_size, LLHost host);
};
-inline LLHost LLPacketRing::getLastSender()
+inline LLHost LLPacketRing::getLastSender() const
{
return mLastSender;
}
-inline LLHost LLPacketRing::getLastReceivingInterface()
+inline LLHost LLPacketRing::getLastReceivingInterface() const
{
return mLastReceivingIF;
}
-
-#endif
diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp
index cfa5178fc6..ad1ff86807 100644
--- a/indra/llmessage/message.cpp
+++ b/indra/llmessage/message.cpp
@@ -656,8 +656,7 @@ bool LLMessageSystem::checkMessages(LockMessageChecker&, S64 frame_count )
// UseCircuitCode is allowed in even from an invalid circuit, so that
// we can toss circuits around.
- if(
- valid_packet &&
+ else if (
!cdp &&
(mTemplateMessageReader->getMessageName() !=
_PREHASH_UseCircuitCode))
@@ -667,8 +666,7 @@ bool LLMessageSystem::checkMessages(LockMessageChecker&, S64 frame_count )
valid_packet = false;
}
- if(
- valid_packet &&
+ if ( valid_packet &&
cdp &&
!cdp->getTrusted() &&
mTemplateMessageReader->isTrusted())
@@ -680,7 +678,7 @@ bool LLMessageSystem::checkMessages(LockMessageChecker&, S64 frame_count )
valid_packet = false;
}
- if( valid_packet )
+ if ( valid_packet )
{
logValidMsg(cdp, host, recv_reliable, recv_resent, acks>0 );
valid_packet = mTemplateMessageReader->readMessage(buffer, host);
@@ -726,6 +724,7 @@ bool LLMessageSystem::checkMessages(LockMessageChecker&, S64 frame_count )
// Check to see if we need to print debug info
if ((mt_sec - mCircuitPrintTime) > mCircuitPrintFreq)
{
+ mPacketRing.dumpPacketRingStats();
dumpCircuitInfo();
mCircuitPrintTime = mt_sec;
}
@@ -821,6 +820,11 @@ void LLMessageSystem::processAcks(LockMessageChecker&, F32 collect_time)
}
}
+S32 LLMessageSystem::drainUdpSocket()
+{
+ return mPacketRing.drainSocket(mSocket);
+}
+
void LLMessageSystem::copyMessageReceivedToSend()
{
// NOTE: babbage: switch builder to match reader to avoid
diff --git a/indra/llmessage/message.h b/indra/llmessage/message.h
index b4b0d94021..1844d5e7cd 100644
--- a/indra/llmessage/message.h
+++ b/indra/llmessage/message.h
@@ -417,6 +417,9 @@ public:
bool checkMessages(LockMessageChecker&, S64 frame_count = 0 );
void processAcks(LockMessageChecker&, F32 collect_time = 0.f);
+ // returns total number of buffered packets after the drain
+ S32 drainUdpSocket();
+
bool isMessageFast(const char *msg);
bool isMessage(const char *msg)
{
diff --git a/indra/llmessage/net.cpp b/indra/llmessage/net.cpp
index f153c938cf..2be5a9e5b6 100644
--- a/indra/llmessage/net.cpp
+++ b/indra/llmessage/net.cpp
@@ -76,14 +76,8 @@ static U32 gsnReceivingIFAddr = INVALID_HOST_IP_ADDRESS; // Address to which dat
const char* LOOPBACK_ADDRESS_STRING = "127.0.0.1";
const char* BROADCAST_ADDRESS_STRING = "255.255.255.255";
-#if LL_DARWIN
- // macOS returns an error when trying to set these to 400000. Smaller values succeed.
- const int SEND_BUFFER_SIZE = 200000;
- const int RECEIVE_BUFFER_SIZE = 200000;
-#else // LL_DARWIN
- const int SEND_BUFFER_SIZE = 400000;
- const int RECEIVE_BUFFER_SIZE = 400000;
-#endif // LL_DARWIN
+const int SEND_BUFFER_SIZE = 200000;
+const int RECEIVE_BUFFER_SIZE = 800000;
// universal functions (cross-platform)
diff --git a/indra/llrender/llfontbitmapcache.cpp b/indra/llrender/llfontbitmapcache.cpp
index 83f5d31186..6a3af1e608 100644
--- a/indra/llrender/llfontbitmapcache.cpp
+++ b/indra/llrender/llfontbitmapcache.cpp
@@ -141,6 +141,7 @@ bool LLFontBitmapCache::nextOpenPos(S32 width, S32& pos_x, S32& pos_y, EFontGlyp
bitmap_num = getNumBitmaps(bitmap_type) - 1;
mCurrentOffsetX[bitmap_idx] += width + 1;
+ mGeneration++;
return true;
}
@@ -168,6 +169,7 @@ void LLFontBitmapCache::reset()
mBitmapWidth = 0;
mBitmapHeight = 0;
+ mGeneration++;
}
//static
diff --git a/indra/llrender/llfontbitmapcache.h b/indra/llrender/llfontbitmapcache.h
index f2dfd87877..0ae4e6bed0 100644
--- a/indra/llrender/llfontbitmapcache.h
+++ b/indra/llrender/llfontbitmapcache.h
@@ -63,6 +63,7 @@ public:
U32 getNumBitmaps(EFontGlyphType bitmapType) const { return (bitmapType < EFontGlyphType::Count) ? static_cast<U32>(mImageRawVec[static_cast<U32>(bitmapType)].size()) : 0U; }
S32 getBitmapWidth() const { return mBitmapWidth; }
S32 getBitmapHeight() const { return mBitmapHeight; }
+ S32 getCacheGeneration() const { return mGeneration; }
protected:
static U32 getNumComponents(EFontGlyphType bitmap_type);
@@ -74,6 +75,7 @@ private:
S32 mCurrentOffsetY[static_cast<U32>(EFontGlyphType::Count)] = { 1 };
S32 mMaxCharWidth = 0;
S32 mMaxCharHeight = 0;
+ S32 mGeneration = 0;
std::vector<LLPointer<LLImageRaw>> mImageRawVec[static_cast<U32>(EFontGlyphType::Count)];
std::vector<LLPointer<LLImageGL>> mImageGLVec[static_cast<U32>(EFontGlyphType::Count)];
};
diff --git a/indra/llrender/llfontfreetype.cpp b/indra/llrender/llfontfreetype.cpp
index 067b13bfb9..723f0d5062 100644
--- a/indra/llrender/llfontfreetype.cpp
+++ b/indra/llrender/llfontfreetype.cpp
@@ -148,7 +148,6 @@ LLFontFreetype::LLFontFreetype()
mIsFallback(false),
mFTFace(NULL),
mRenderGlyphCount(0),
- mAddGlyphCount(0),
mStyle(0),
mPointSize(0)
{
@@ -576,7 +575,6 @@ LLFontGlyphInfo* LLFontFreetype::addGlyphFromFont(const LLFontFreetype *fontp, l
S32 pos_x, pos_y;
U32 bitmap_num;
mFontBitmapCachep->nextOpenPos(width, pos_x, pos_y, bitmap_glyph_type, bitmap_num);
- mAddGlyphCount++;
LLFontGlyphInfo* gi = new LLFontGlyphInfo(glyph_index, requested_glyph_type);
gi->mXBitmapOffset = pos_x;
diff --git a/indra/llrender/llfontfreetype.h b/indra/llrender/llfontfreetype.h
index a2d925c5f6..783bf4a4b3 100644
--- a/indra/llrender/llfontfreetype.h
+++ b/indra/llrender/llfontfreetype.h
@@ -148,7 +148,6 @@ public:
void setStyle(U8 style);
U8 getStyle() const;
- S32 getAddedGlyphs() const { return mAddGlyphCount; }
private:
void resetBitmapCache();
@@ -188,7 +187,6 @@ private:
mutable LLFontBitmapCache* mFontBitmapCachep;
mutable S32 mRenderGlyphCount;
- mutable S32 mAddGlyphCount;
};
#endif // LL_FONTFREETYPE_H
diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp
index 4037c036e5..16eec1fdd2 100644
--- a/indra/llrender/llfontgl.cpp
+++ b/indra/llrender/llfontgl.cpp
@@ -110,9 +110,10 @@ S32 LLFontGL::getNumFaces(const std::string& filename)
return mFontFreetype->getNumFaces(filename);
}
-S32 LLFontGL::getKnownGlyphCount() const
+S32 LLFontGL::getCacheGeneration() const
{
- return mFontFreetype ? mFontFreetype->getAddedGlyphs() : 0;
+ const LLFontBitmapCache* font_bitmap_cache = mFontFreetype->getFontBitmapCache();
+ return font_bitmap_cache->getCacheGeneration();
}
S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, const LLRect& rect, const LLColor4 &color, HAlign halign, VAlign valign, U8 style,
diff --git a/indra/llrender/llfontgl.h b/indra/llrender/llfontgl.h
index 73efc6b1eb..1c8e036f58 100644
--- a/indra/llrender/llfontgl.h
+++ b/indra/llrender/llfontgl.h
@@ -90,7 +90,7 @@ public:
bool loadFace(const std::string& filename, F32 point_size, const F32 vert_dpi, const F32 horz_dpi, bool is_fallback, S32 face_n);
S32 getNumFaces(const std::string& filename);
- S32 getKnownGlyphCount() const;
+ S32 getCacheGeneration() const;
S32 render(const LLWString &text, S32 begin_offset,
const LLRect& rect,
diff --git a/indra/llrender/llfontvertexbuffer.cpp b/indra/llrender/llfontvertexbuffer.cpp
index b53a841a87..a223509d30 100644
--- a/indra/llrender/llfontvertexbuffer.cpp
+++ b/indra/llrender/llfontvertexbuffer.cpp
@@ -148,7 +148,7 @@ S32 LLFontVertexBuffer::render(
|| mLastHorizDPI != LLFontGL::sHorizDPI
|| mLastOrigin != LLFontGL::sCurOrigin
|| mLastResGeneration != LLFontGL::sResolutionGeneration
- || mLastFontGlyphCount != fontp->getKnownGlyphCount())
+ || mLastFontCacheGen != fontp->getCacheGeneration())
{
genBuffers(fontp, text, begin_offset, x, y, color, halign, valign,
style, shadow, max_chars, max_pixels, right_x, use_ellipses, use_color);
@@ -180,6 +180,9 @@ void LLFontVertexBuffer::genBuffers(
{
// todo: add a debug build assert if this triggers too often for to long?
mBufferList.clear();
+ // Save before rendreing, it can change mid-render,
+ // so will need to rerender previous characters
+ mLastFontCacheGen = fontp->getCacheGeneration();
gGL.beginList(&mBufferList);
mChars = fontp->render(text, begin_offset, x, y, color, halign, valign,
@@ -204,7 +207,6 @@ void LLFontVertexBuffer::genBuffers(
mLastHorizDPI = LLFontGL::sHorizDPI;
mLastOrigin = LLFontGL::sCurOrigin;
mLastResGeneration = LLFontGL::sResolutionGeneration;
- mLastFontGlyphCount = fontp->getKnownGlyphCount();
if (right_x)
{
diff --git a/indra/llrender/llfontvertexbuffer.h b/indra/llrender/llfontvertexbuffer.h
index d5e1280dbf..a9e1e2337c 100644
--- a/indra/llrender/llfontvertexbuffer.h
+++ b/indra/llrender/llfontvertexbuffer.h
@@ -122,7 +122,7 @@ private:
// Adding new characters to bitmap cache can alter value from getBitmapWidth();
// which alters whole string. So rerender when new characters were added to cache.
- S32 mLastFontGlyphCount = 0;
+ S32 mLastFontCacheGen = 0;
static bool sEnableBufferCollection;
};
diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp
index 17fa8f14a9..3cdbb139be 100644
--- a/indra/llrender/llgl.cpp
+++ b/indra/llrender/llgl.cpp
@@ -2475,12 +2475,15 @@ void LLGLState::checkStates(GLboolean writeAlpha)
return;
}
- GLint src;
- GLint dst;
- glGetIntegerv(GL_BLEND_SRC, &src);
- glGetIntegerv(GL_BLEND_DST, &dst);
- llassert_always(src == GL_SRC_ALPHA);
- llassert_always(dst == GL_ONE_MINUS_SRC_ALPHA);
+ GLint srcRGB, dstRGB, srcAlpha, dstAlpha;
+ glGetIntegerv(GL_BLEND_SRC_RGB, &srcRGB);
+ glGetIntegerv(GL_BLEND_DST_RGB, &dstRGB);
+ glGetIntegerv(GL_BLEND_SRC_ALPHA, &srcAlpha);
+ glGetIntegerv(GL_BLEND_DST_ALPHA, &dstAlpha);
+ llassert_always(srcRGB == GL_SRC_ALPHA);
+ llassert_always(srcAlpha == GL_SRC_ALPHA);
+ llassert_always(dstRGB == GL_ONE_MINUS_SRC_ALPHA);
+ llassert_always(dstAlpha == GL_ONE_MINUS_SRC_ALPHA);
// disable for now until usage is consistent
//GLboolean colorMask[4];
diff --git a/indra/llui/llfolderview.cpp b/indra/llui/llfolderview.cpp
index a059c4d741..b664065532 100644
--- a/indra/llui/llfolderview.cpp
+++ b/indra/llui/llfolderview.cpp
@@ -2011,7 +2011,7 @@ void LLFolderView::onIdleUpdateMenu(void* user_data)
self->updateMenuOptions(menu);
menu->needsArrange(); // update menu height if needed
}
- gIdleCallbacks.deleteFunction(onIdleUpdateMenu, NULL);
+ gIdleCallbacks.deleteFunction(onIdleUpdateMenu, self);
}
bool LLFolderView::isFolderSelected()
diff --git a/indra/llxml/llxmlnode.cpp b/indra/llxml/llxmlnode.cpp
index 48805f8240..8a2c36a307 100644
--- a/indra/llxml/llxmlnode.cpp
+++ b/indra/llxml/llxmlnode.cpp
@@ -309,6 +309,7 @@ void LLXMLNode::addChild(LLXMLNodePtr& new_child)
// virtual
LLXMLNodePtr LLXMLNode::createChild(const char* name, bool is_attribute)
{
+ // Todo: validate to make sure node name is valid? (no spaces, etc)
return createChild(gStringTable.addStringEntry(name), is_attribute);
}
diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt
index 0f9f025fe4..991d8e5c5f 100644
--- a/indra/newview/VIEWER_VERSION.txt
+++ b/indra/newview/VIEWER_VERSION.txt
@@ -1 +1 @@
-7.1.12
+7.1.13
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 1aec56447d..7b764d47b3 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -7860,6 +7860,17 @@
<key>Value</key>
<integer>0</integer>
</map>
+ <key>RenderTextureVRAMDivisor</key>
+ <map>
+ <key>Comment</key>
+ <string>Divisor for maximum amount of VRAM the viewer will use for textures. 1 = all the VRAM. Used in conjunction with RenderMaxVRAMBudget.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>U32</string>
+ <key>Value</key>
+ <integer>2</integer>
+ </map>
<key>RenderMinFreeMainMemoryThreshold</key>
<map>
<key>Comment</key>
@@ -11684,7 +11695,7 @@
<key>Type</key>
<string>F32</string>
<key>Value</key>
- <real>0.04</real>
+ <real>0.0095</real>
</map>
<key>TextureScaleMaxAreaFactor</key>
<map>
diff --git a/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl b/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl
index d32455d70c..fa94fb98f0 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl
@@ -80,6 +80,17 @@ vec3 atmosFragLightingLinear(vec3 light, vec3 additive, vec3 atten);
vec4 decodeNormal(vec4 norm);
+vec3 clampHDRRange(vec3 color)
+{
+ // Why do this?
+ // There are situations where the color range will go to something insane - potentially producing infs and NaNs even.
+ // This is a safety measure to prevent that.
+ // As to the specific number there - allegedly some HDR displays expect values to be in the 0-11.2 range. Citation needed.
+ // -Geenz 2025-03-05
+ color = mix(color, vec3(0.0), isnan(color));
+ return clamp(color, vec3(0.0), vec3(11.2));
+}
+
float calcLegacyDistanceAttenuation(float distance, float falloff)
{
float dist_atten = 1.0 - clamp((distance + falloff)/(1.0 + falloff), 0.0, 1.0);
diff --git a/indra/newview/app_settings/shaders/class1/deferred/postDeferredF.glsl b/indra/newview/app_settings/shaders/class1/deferred/postDeferredF.glsl
index 9797bcd2ce..4e737492a7 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/postDeferredF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/postDeferredF.glsl
@@ -69,6 +69,8 @@ void dofSampleNear(inout vec4 diff, inout float w, float min_sc, vec2 tc)
w += wg;
}
+vec3 clampHDRRange(vec3 color);
+
void main()
{
vec2 tc = vary_fragcoord.xy;
@@ -120,5 +122,6 @@ void main()
diff /= w;
}
+ diff.rgb = clampHDRRange(diff.rgb);
frag_color = diff;
}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/postDeferredGammaCorrect.glsl b/indra/newview/app_settings/shaders/class1/deferred/postDeferredGammaCorrect.glsl
index befd2ae6da..4ccc6f54a8 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/postDeferredGammaCorrect.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/postDeferredGammaCorrect.glsl
@@ -43,6 +43,8 @@ vec3 legacyGamma(vec3 color)
return c;
}
+vec3 clampHDRRange(vec3 color);
+
void main()
{
//this is the one of the rare spots where diffuseRect contains linear color values (not sRGB)
@@ -53,6 +55,7 @@ void main()
diff.rgb = legacyGamma(diff.rgb);
#endif
- frag_color = max(diff, vec4(0));
+ diff.rgb = clampHDRRange(diff.rgb);
+ frag_color = diff;
}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/postDeferredNoDoFF.glsl b/indra/newview/app_settings/shaders/class1/deferred/postDeferredNoDoFF.glsl
index 32b0a1ac8e..c05b4eed7a 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/postDeferredNoDoFF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/postDeferredNoDoFF.glsl
@@ -71,6 +71,7 @@ float noise(vec2 x) {
//=============================
+vec3 clampHDRRange(vec3 color);
void main()
@@ -84,6 +85,7 @@ void main()
diff.rgb += nz*0.003;
#endif
+ diff.rgb = clampHDRRange(diff.rgb);
frag_color = diff;
gl_FragDepth = texture(depthMap, vary_fragcoord.xy).r;
diff --git a/indra/newview/app_settings/shaders/class1/deferred/postDeferredTonemap.glsl b/indra/newview/app_settings/shaders/class1/deferred/postDeferredTonemap.glsl
index c4610bffac..1f01c7f16a 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/postDeferredTonemap.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/postDeferredTonemap.glsl
@@ -34,6 +34,8 @@ in vec2 vary_fragcoord;
vec3 linear_to_srgb(vec3 cl);
vec3 toneMap(vec3 color);
+vec3 clampHDRRange(vec3 color);
+
void main()
{
//this is the one of the rare spots where diffuseRect contains linear color values (not sRGB)
@@ -45,6 +47,7 @@ void main()
diff.rgb = clamp(diff.rgb, vec3(0.0), vec3(1.0));
#endif
+ diff.rgb = clampHDRRange(diff.rgb);
//debugExposure(diff.rgb);
frag_color = max(diff, vec4(0));
}
diff --git a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl
index 2e288184bf..6cec65ad83 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl
@@ -104,6 +104,7 @@ vec3 pbrBaseLight(vec3 diffuseColor,
vec3 atten);
GBufferInfo getGBuffer(vec2 screenpos);
+vec3 clampHDRRange(vec3 color);
void adjustIrradiance(inout vec3 irradiance, float ambocc)
{
@@ -278,6 +279,7 @@ void main()
float final_scale = 1;
if (classic_mode > 0)
final_scale = 1.1;
- frag_color.rgb = max(color.rgb * final_scale, vec3(0)); //output linear since local lights will be added to this shader's results
+
+ frag_color.rgb = clampHDRRange(color.rgb * final_scale); //output linear since local lights will be added to this shader's results
frag_color.a = 0.0;
}
diff --git a/indra/newview/app_settings/shaders/class3/environment/waterF.glsl b/indra/newview/app_settings/shaders/class3/environment/waterF.glsl
index 18dc367568..a194832b1d 100644
--- a/indra/newview/app_settings/shaders/class3/environment/waterF.glsl
+++ b/indra/newview/app_settings/shaders/class3/environment/waterF.glsl
@@ -255,7 +255,7 @@ void main()
shadow = sampleDirectionalShadow(pos.xyz, norm.xyz, distort);
#endif
- vec3 sunlit_linear = srgb_to_linear(sunlit);
+ vec3 sunlit_linear = sunlit;
float fade = 1.0;
#ifdef TRANSPARENT_WATER
float depth = texture(depthMap, distort).r;
@@ -263,7 +263,12 @@ void main()
vec3 refPos = getPositionWithNDC(vec3(distort*2.0-vec2(1.0), depth*2.0-1.0));
// Calculate some distance fade in the water to better assist with refraction blending and reducing the refraction texture's "disconnect".
- fade = max(0.0,min(1.0, (pos.z - refPos.z) / 10.0)) * water_mask;
+#ifdef SHORELINE_FADE
+ fade = max(0.0,min(1.0, (pos.z - refPos.z) / 10.0))
+#else
+ fade = 1.0 * water_mask;
+#endif
+
distort2 = mix(distort, distort2, min(1.0, fade * 10.0));
depth = texture(depthMap, distort2).r;
@@ -317,7 +322,7 @@ void main()
pbrPunctual(diffuseColor, specularColor, perceptualRoughness, metallic, normalize(wavef+up*max(dist, 32.0)/32.0*(1.0-vdu)), v, normalize(light_dir), nl, diffPunc, specPunc);
- vec3 punctual = clamp(nl * (diffPunc + specPunc), vec3(0), vec3(10)) * sunlit_linear * shadow;
+ vec3 punctual = clamp(nl * (diffPunc + specPunc), vec3(0), vec3(10)) * sunlit_linear * shadow * atten;
radiance *= df2.y;
//radiance = toneMapNoExposure(radiance);
vec3 color = vec3(0);
diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp
index aca0775fe8..43e8fa3e3c 100644
--- a/indra/newview/gltfscenemanager.cpp
+++ b/indra/newview/gltfscenemanager.cpp
@@ -356,8 +356,9 @@ void GLTFSceneManager::addGLTFObject(LLViewerObject* obj, LLUUID gltf_id)
llassert(obj->getVolume()->getParams().getSculptID() == gltf_id);
llassert(obj->getVolume()->getParams().getSculptType() == LL_SCULPT_TYPE_GLTF);
- if (obj->mGLTFAsset)
- { // object already has a GLTF asset, don't reload it
+ if (obj->mGLTFAsset || obj->mIsGLTFAssetMissing )
+ {
+ // object already has a GLTF asset or load failed, don't reload it
// TODO: below assertion fails on dupliate requests for assets -- possibly need to touch up asset loading state machine
// llassert(std::find(mObjects.begin(), mObjects.end(), obj) != mObjects.end());
@@ -398,16 +399,19 @@ void GLTFSceneManager::onGLTFBinLoadComplete(const LLUUID& id, LLAssetType::ETyp
}
else
{
- LL_WARNS("GLTF") << "Failed to prepare GLTF asset: " << id << LL_ENDL;
+ LL_WARNS("GLTF") << "Failed to prepare GLTF asset: " << id << ". Marking as missing." << LL_ENDL;
+ obj->mIsGLTFAssetMissing = true;
obj->mGLTFAsset = nullptr;
}
}
}
+ obj->unref(); // todo: use LLPointer
}
}
else
{
- LL_WARNS("GLTF") << "Failed to load GLTF asset: " << id << LL_ENDL;
+ LL_WARNS("GLTF") << "Failed to load GLTF asset: " << id << ". Marking as missing." << LL_ENDL;
+ obj->mIsGLTFAssetMissing = true;
obj->unref();
}
});
@@ -446,7 +450,8 @@ void GLTFSceneManager::onGLTFLoadComplete(const LLUUID& id, LLAssetType::EType a
}
else
{
- LL_WARNS("GLTF") << "Buffer URI is not a valid UUID: " << buffer.mUri << LL_ENDL;
+ LL_WARNS("GLTF") << "Buffer URI is not a valid UUID: " << buffer.mUri << " for asset id: " << id << ". Marking as missing." << LL_ENDL;
+ obj->mIsGLTFAssetMissing = true;
obj->unref();
return;
}
@@ -455,7 +460,8 @@ void GLTFSceneManager::onGLTFLoadComplete(const LLUUID& id, LLAssetType::EType a
}
else
{
- LL_WARNS("GLTF") << "Failed to load GLTF asset: " << id << LL_ENDL;
+ LL_WARNS("GLTF") << "Failed to load GLTF asset: " << id << ". Marking as missing." << LL_ENDL;
+ obj->mIsGLTFAssetMissing = true;
obj->unref();
}
}
@@ -517,6 +523,7 @@ void GLTFSceneManager::update()
if (mUploadingObject)
{
mUploadingObject->mGLTFAsset = nullptr;
+ mUploadingObject->mIsGLTFAssetMissing = false;
mUploadingObject->setGLTFAsset(assetId);
mUploadingObject->markForUpdate();
mUploadingObject = nullptr;
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index 8756baa04a..a076594e0a 100644
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -2501,7 +2501,10 @@ void LLAgent::endAnimationUpdateUI()
gAgentAvatarp->updateAttachmentVisibility(gAgentCamera.getCameraMode());
}
- gFloaterTools->dirty();
+ if (gFloaterTools)
+ {
+ gFloaterTools->dirty();
+ }
// Don't let this be called more than once if the camera
// mode hasn't changed. --JC
diff --git a/indra/newview/llappdelegate-objc.mm b/indra/newview/llappdelegate-objc.mm
index 1090888c1c..d4b05dde72 100644
--- a/indra/newview/llappdelegate-objc.mm
+++ b/indra/newview/llappdelegate-objc.mm
@@ -301,7 +301,8 @@ struct AttachmentInfo
AttachmentInfo(metadata.logFilePathname, "text/plain"),
AttachmentInfo(metadata.userSettingsPathname, "text/xml"),
AttachmentInfo(metadata.accountSettingsPathname, "text/xml"),
- AttachmentInfo(metadata.staticDebugPathname, "text/xml")
+ AttachmentInfo(metadata.staticDebugPathname, "text/xml"),
+ AttachmentInfo(metadata.attributesPathname, "text/xml")
};
secondLogPath = metadata.secondLogFilePathname;
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 430b42acf6..43ea083adf 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -358,10 +358,6 @@ std::string gLastVersionChannel;
LLVector3 gWindVec(3.0, 3.0, 0.0);
LLVector3 gRelativeWindVec(0.0, 0.0, 0.0);
-U32 gPacketsIn = 0;
-
-bool gPrintMessagesThisFrame = false;
-
bool gRandomizeFramerate = false;
bool gPeriodicSlowFrame = false;
@@ -370,6 +366,7 @@ bool gLLErrorActivated = false;
bool gLogoutInProgress = false;
bool gSimulateMemLeak = false;
+bool gDoDisconnect = false;
// We don't want anyone, especially threads working on the graphics pipeline,
// to have to block due to this WorkQueue being full.
@@ -383,7 +380,6 @@ const std::string MARKER_FILE_NAME("SecondLife.exec_marker");
const std::string START_MARKER_FILE_NAME("SecondLife.start_marker");
const std::string ERROR_MARKER_FILE_NAME("SecondLife.error_marker");
const std::string LOGOUT_MARKER_FILE_NAME("SecondLife.logout_marker");
-static bool gDoDisconnect = false;
static std::string gLaunchFileOnQuit;
// Used on Win32 for other apps to identify our window (eg, win_setup)
@@ -1533,9 +1529,9 @@ bool LLAppViewer::doFrame()
{
LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df pauseMainloopTimeout");
- pingMainloopTimeout("Main:Sleep");
+ pingMainloopTimeout("Main:Sleep");
- pauseMainloopTimeout();
+ pauseMainloopTimeout();
}
// Sleep and run background threads
@@ -4297,7 +4293,7 @@ U32 LLAppViewer::getTextureCacheVersion()
U32 LLAppViewer::getDiskCacheVersion()
{
// Viewer disk cache version intorduced in Simple Cache Viewer, change if the cache format changes.
- const U32 DISK_CACHE_VERSION = 1;
+ const U32 DISK_CACHE_VERSION = 2;
return DISK_CACHE_VERSION ;
}
@@ -4956,6 +4952,20 @@ void LLAppViewer::idle()
if (gTeleportDisplay)
{
+ if (gAgent.getTeleportState() == LLAgent::TELEPORT_ARRIVING)
+ {
+ // Teleported, but waiting for things to load, start processing surface data
+ {
+ LL_RECORD_BLOCK_TIME(FTM_NETWORK);
+ gVLManager.unpackData();
+ }
+ {
+ LL_RECORD_BLOCK_TIME(FTM_REGION_UPDATE);
+ const F32 max_region_update_time = .001f; // 1ms
+ LLWorld::getInstance()->updateRegions(max_region_update_time);
+ }
+ }
+
return;
}
@@ -5392,12 +5402,9 @@ void LLAppViewer::idleNameCache()
// Handle messages, and all message related stuff
//
-#define TIME_THROTTLE_MESSAGES
-#ifdef TIME_THROTTLE_MESSAGES
-#define CHECK_MESSAGES_DEFAULT_MAX_TIME .020f // 50 ms = 50 fps (just for messages!)
+constexpr F32 CHECK_MESSAGES_DEFAULT_MAX_TIME = 0.020f; // 50 ms = 50 fps (just for messages!)
static F32 CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME;
-#endif
static LLTrace::BlockTimerStatHandle FTM_IDLE_NETWORK("Idle Network");
static LLTrace::BlockTimerStatHandle FTM_MESSAGE_ACKS("Message Acks");
@@ -5424,6 +5431,7 @@ void LLAppViewer::idleNetwork()
F32 total_time = 0.0f;
{
+ bool needs_drain = false;
LockMessageChecker lmc(gMessageSystem);
while (lmc.checkAllMessages(frame_count, gServicePump))
{
@@ -5436,54 +5444,44 @@ void LLAppViewer::idleNetwork()
}
total_decoded++;
- gPacketsIn++;
if (total_decoded > MESSAGE_MAX_PER_FRAME)
{
+ needs_drain = true;
break;
}
-#ifdef TIME_THROTTLE_MESSAGES
// Prevent slow packets from completely destroying the frame rate.
// This usually happens due to clumps of avatars taking huge amount
// of network processing time (which needs to be fixed, but this is
// a good limit anyway).
total_time = check_message_timer.getElapsedTimeF32();
if (total_time >= CheckMessagesMaxTime)
+ {
+ needs_drain = true;
break;
-#endif
+ }
+ }
+ if (needs_drain || gMessageSystem->mPacketRing.getNumBufferedPackets() > 0)
+ {
+ // Rather than allow packets to silently backup on the socket
+ // we drain them into our own buffer so we know how many exist.
+ S32 num_buffered_packets = gMessageSystem->drainUdpSocket();
+ if (num_buffered_packets > 0)
+ {
+ // Increase CheckMessagesMaxTime so that we will eventually catch up
+ CheckMessagesMaxTime *= 1.035f; // 3.5% ~= 2x in 20 frames, ~8x in 60 frames
+ }
+ }
+ else
+ {
+ // Reset CheckMessagesMaxTime to default value
+ CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME;
}
// Handle per-frame message system processing.
lmc.processAcks(gSavedSettings.getF32("AckCollectTime"));
}
-
-#ifdef TIME_THROTTLE_MESSAGES
- if (total_time >= CheckMessagesMaxTime)
- {
- // Increase CheckMessagesMaxTime so that we will eventually catch up
- CheckMessagesMaxTime *= 1.035f; // 3.5% ~= x2 in 20 frames, ~8x in 60 frames
- }
- else
- {
- // Reset CheckMessagesMaxTime to default value
- CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME;
- }
-#endif
-
- // Decode enqueued messages...
- S32 remaining_possible_decodes = MESSAGE_MAX_PER_FRAME - total_decoded;
-
- if( remaining_possible_decodes <= 0 )
- {
- LL_INFOS() << "Maxed out number of messages per frame at " << MESSAGE_MAX_PER_FRAME << LL_ENDL;
- }
-
- if (gPrintMessagesThisFrame)
- {
- LL_INFOS() << "Decoded " << total_decoded << " msgs this frame!" << LL_ENDL;
- gPrintMessagesThisFrame = false;
- }
}
add(LLStatViewer::NUM_NEW_OBJECTS, gObjectList.mNumNewObjects);
@@ -5664,6 +5662,27 @@ void LLAppViewer::forceErrorCoroutineCrash()
LLCoros::instance().launch("LLAppViewer::crashyCoro", [] {throw LLException("A deliberate crash from LLCoros"); });
}
+void LLAppViewer::forceErrorCoroprocedureCrash()
+{
+ LL_WARNS() << "Forcing a crash in LLCoprocedureManager" << LL_ENDL;
+ LLCoprocedureManager::instance().enqueueCoprocedure("Upload", "DeliberateCrash",
+ [](LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t&, const LLUUID&)
+ {
+ LL_WARNS() << "Forcing a deliberate bad memory access from LLCoprocedureManager" << LL_ENDL;
+ S32* crash = NULL;
+ *crash = 0xDEADBEEF;
+ });
+}
+
+void LLAppViewer::forceErrorWorkQueueCrash()
+{
+ LL::WorkQueue::ptr_t workqueue = LL::WorkQueue::getInstance("General");
+ if (workqueue)
+ {
+ workqueue->post([]() { throw LLException("This is a deliberate crash from General Queue"); });
+ }
+}
+
void LLAppViewer::forceErrorThreadCrash()
{
class LLCrashTestThread : public LLThread
diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h
index b4756eecd6..3da0246ccf 100644
--- a/indra/newview/llappviewer.h
+++ b/indra/newview/llappviewer.h
@@ -157,9 +157,6 @@ public:
void loadNameCache();
void saveNameCache();
- void loadExperienceCache();
- void saveExperienceCache();
-
void removeMarkerFiles();
void recordSessionToMarker();
@@ -175,6 +172,8 @@ public:
virtual void forceErrorOSSpecificException();
virtual void forceErrorDriverCrash();
virtual void forceErrorCoroutineCrash();
+ virtual void forceErrorCoroprocedureCrash();
+ virtual void forceErrorWorkQueueCrash();
virtual void forceErrorThreadCrash();
// The list is found in app_settings/settings_files.xml
@@ -411,11 +410,10 @@ extern std::string gLastVersionChannel;
extern LLVector3 gWindVec;
extern LLVector3 gRelativeWindVec;
-extern U32 gPacketsIn;
-extern bool gPrintMessagesThisFrame;
extern bool gRandomizeFramerate;
extern bool gPeriodicSlowFrame;
+extern bool gDoDisconnect;
extern bool gSimulateMemLeak;
diff --git a/indra/newview/llappviewermacosx-for-objc.h b/indra/newview/llappviewermacosx-for-objc.h
index 5a69cd93fc..c3f1e35872 100644
--- a/indra/newview/llappviewermacosx-for-objc.h
+++ b/indra/newview/llappviewermacosx-for-objc.h
@@ -41,6 +41,7 @@ void clearDumpLogsDir();
struct CrashMetadata
{
std::string logFilePathname;
+ std::string attributesPathname;
std::string userSettingsPathname;
std::string accountSettingsPathname;
std::string staticDebugPathname;
diff --git a/indra/newview/llappviewermacosx.cpp b/indra/newview/llappviewermacosx.cpp
index 4162c0479a..aab6d00573 100644
--- a/indra/newview/llappviewermacosx.cpp
+++ b/indra/newview/llappviewermacosx.cpp
@@ -172,6 +172,8 @@ CrashMetadataSingleton::CrashMetadataSingleton()
// Note: we depend on being able to read the static_debug_info.log file
// from the *previous* run before we overwrite it with the new one for
// *this* run. LLAppViewer initialization must happen in the Right Order.
+
+ // Todo: consider converting static file into bugspalt attributes file
staticDebugPathname = *gViewerAppPtr->getStaticDebugFile();
std::ifstream static_file(staticDebugPathname);
LLSD info;
@@ -215,7 +217,32 @@ CrashMetadataSingleton::CrashMetadataSingleton()
}
}
}
+
+ // Populate bugsplat attributes
+ LLXMLNodePtr out_node = new LLXMLNode("XmlCrashContext", false);
+
+ out_node->createChild("OS", false)->setValue(OSInfo);
+ out_node->createChild("AppState", false)->setValue(info["StartupState"].asString());
+ out_node->createChild("GraphicsCard", false)->setValue(info["GraphicsCard"].asString());
+ out_node->createChild("GLVersion", false)->setValue(info["GLInfo"]["GLVersion"].asString());
+ out_node->createChild("GLRenderer", false)->setValue(info["GLInfo"]["GLRenderer"].asString());
+ out_node->createChild("RAM", false)->setValue(info["RAMInfo"]["Physical"].asString());
+
+ if (!out_node->isNull())
+ {
+ attributesPathname = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "CrashContext.xml");
+ LLFILE* fp = LLFile::fopen(attributesPathname, "w");
+
+ if (fp != NULL)
+ {
+ LLXMLNode::writeHeaderToFile(fp);
+ out_node->writeToFile(fp);
+
+ fclose(fp);
+ }
+ }
}
+ // else Todo: consider fillig at least some values, like OS
}
// Avoid having to compile all of our LLSingleton machinery in Objective-C++.
diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp
index 4d2c58bdab..6e2f4ef670 100644
--- a/indra/newview/llappviewerwin32.cpp
+++ b/indra/newview/llappviewerwin32.cpp
@@ -776,14 +776,16 @@ bool LLAppViewerWin32::cleanup()
return result;
}
-void LLAppViewerWin32::reportCrashToBugsplat(void* pExcepInfo)
+bool LLAppViewerWin32::reportCrashToBugsplat(void* pExcepInfo)
{
#if defined(LL_BUGSPLAT)
if (sBugSplatSender)
{
sBugSplatSender->createReport((EXCEPTION_POINTERS*)pExcepInfo);
+ return true;
}
#endif // LL_BUGSPLAT
+ return false;
}
void LLAppViewerWin32::initLoggingAndGetLastDuration()
diff --git a/indra/newview/llappviewerwin32.h b/indra/newview/llappviewerwin32.h
index 2242c95b06..250e72edf3 100644
--- a/indra/newview/llappviewerwin32.h
+++ b/indra/newview/llappviewerwin32.h
@@ -43,7 +43,7 @@ public:
bool init() override; // Override to do application initialization
bool cleanup() override;
- void reportCrashToBugsplat(void* pExcepInfo) override;
+ bool reportCrashToBugsplat(void* pExcepInfo) override;
protected:
void initLoggingAndGetLastDuration() override; // Override to clean stack_trace info.
diff --git a/indra/newview/llavatarpropertiesprocessor.cpp b/indra/newview/llavatarpropertiesprocessor.cpp
index b14d1d7d26..a2e81ed255 100644
--- a/indra/newview/llavatarpropertiesprocessor.cpp
+++ b/indra/newview/llavatarpropertiesprocessor.cpp
@@ -671,6 +671,7 @@ void LLAvatarPropertiesProcessor::sendClassifiedInfoUpdate(const LLAvatarClassif
void LLAvatarPropertiesProcessor::sendPickInfoRequest(const LLUUID& creator_id, const LLUUID& pick_id)
{
+ LL_DEBUGS("PickInfo") << " Requiesting pick info for " << pick_id << LL_ENDL;
// Must ask for a pick based on the creator id because
// the pick database is distributed to the inventory cluster. JC
std::vector<std::string> request_params{ creator_id.asString(), pick_id.asString() };
diff --git a/indra/newview/lldrawpoolwater.cpp b/indra/newview/lldrawpoolwater.cpp
index 32de0e5ee7..7d58511d41 100644
--- a/indra/newview/lldrawpoolwater.cpp
+++ b/indra/newview/lldrawpoolwater.cpp
@@ -213,7 +213,7 @@ void LLDrawPoolWater::renderPostDeferred(S32 pass)
else if (tex_b && !tex_a)
{
shader->bindTexture(LLViewerShaderMgr::BUMP_MAP, tex_b);
- tex_a->setFilteringOption(filter_mode);
+ tex_b->setFilteringOption(filter_mode);
blend_factor = 0; // only one tex provided, no blending
}
else if (tex_b != tex_a)
diff --git a/indra/newview/llfloaterlagmeter.cpp b/indra/newview/llfloaterlagmeter.cpp
index 28fa8dea9a..1377526f69 100644
--- a/indra/newview/llfloaterlagmeter.cpp
+++ b/indra/newview/llfloaterlagmeter.cpp
@@ -200,6 +200,7 @@ void LLFloaterLagMeter::determineNetwork()
// the network handlers are de-synched from the rendering.
F32Milliseconds client_frame_time = frame_recording.getPeriodMean(LLStatViewer::FRAME_STACKTIME);
+ // Todo: account for LLPacketRing dropped packets? viewer drops those when it can't keep up
if(packet_loss >= mNetworkPacketLossCritical)
{
mNetworkButton->setImageUnselected(LLUI::getUIImage(LAG_CRITICAL_IMAGE_NAME));
diff --git a/indra/newview/llgiveinventory.cpp b/indra/newview/llgiveinventory.cpp
index 1259b65ebe..57dd203f2f 100644
--- a/indra/newview/llgiveinventory.cpp
+++ b/indra/newview/llgiveinventory.cpp
@@ -414,7 +414,11 @@ void LLGiveInventory::commitGiveInventoryItem(const LLUUID& to_agent,
effectp->setTargetObject(gObjectList.findObject(to_agent));
effectp->setDuration(LL_HUD_DUR_SHORT);
effectp->setColor(LLColor4U(gAgent.getEffectColor()));
- gFloaterTools->dirty();
+
+ if (gFloaterTools)
+ {
+ gFloaterTools->dirty();
+ }
LLMuteList::getInstance()->autoRemove(to_agent, LLMuteList::AR_INVENTORY);
@@ -572,7 +576,11 @@ bool LLGiveInventory::commitGiveInventoryCategory(const LLUUID& to_agent,
effectp->setTargetObject(gObjectList.findObject(to_agent));
effectp->setDuration(LL_HUD_DUR_SHORT);
effectp->setColor(LLColor4U(gAgent.getEffectColor()));
- gFloaterTools->dirty();
+
+ if (gFloaterTools)
+ {
+ gFloaterTools->dirty();
+ }
LLMuteList::getInstance()->autoRemove(to_agent, LLMuteList::AR_INVENTORY);
diff --git a/indra/newview/llheroprobemanager.cpp b/indra/newview/llheroprobemanager.cpp
index cf606e8863..343bb01072 100644
--- a/indra/newview/llheroprobemanager.cpp
+++ b/indra/newview/llheroprobemanager.cpp
@@ -80,6 +80,17 @@ void LLHeroProbeManager::update()
return;
}
+ // Part of a hacky workaround to fix #3331.
+ // For some reason clearing shaders will cause mirrors to actually work.
+ // There's likely some deeper state issue that needs to be resolved.
+ // - Geenz 2025-02-25
+ if (!mInitialized && LLStartUp::getStartupState() > STATE_PRECACHE)
+ {
+ LLViewerShaderMgr::instance()->clearShaderCache();
+ LLViewerShaderMgr::instance()->setShaders();
+ mInitialized = true;
+ }
+
LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
llassert(!gCubeSnapshot); // assert a snapshot is not in progress
if (LLAppViewer::instance()->logoutRequestSent())
diff --git a/indra/newview/llheroprobemanager.h b/indra/newview/llheroprobemanager.h
index 58a94a3de8..2737ec5ddf 100644
--- a/indra/newview/llheroprobemanager.h
+++ b/indra/newview/llheroprobemanager.h
@@ -144,6 +144,7 @@ private:
std::vector<LLPointer<LLVOVolume>> mHeroVOList;
LLPointer<LLVOVolume> mNearestHero;
-
+ // Part of a hacky workaround to fix #3331.
+ bool mInitialized = false;
};
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 93453f507c..e1fa84b4d8 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -255,12 +255,12 @@
// mSkinInfoQ mMutex rw.repo.mMutex, rw.main.mMutex [5] (was: [0])
// mDecompositionRequests mMutex rw.repo.mMutex, ro.repo.none [5]
// mPhysicsShapeRequests mMutex rw.repo.mMutex, ro.repo.none [5]
-// mDecompositionQ mMutex rw.repo.mMutex, rw.main.mMutex [5] (was: [0])
+// mDecompositionQ mMutex rw.repo.mLoadedMutex, rw.main.mLoadedMutex [5] (was: [0])
// mHeaderReqQ mMutex ro.repo.none [5], rw.repo.mMutex, rw.any.mMutex
// mLODReqQ mMutex ro.repo.none [5], rw.repo.mMutex, rw.any.mMutex
-// mUnavailableQ mMutex rw.repo.none [0], ro.main.none [5], rw.main.mMutex
-// mLoadedQ mMutex rw.repo.mMutex, ro.main.none [5], rw.main.mMutex
-// mPendingLOD mMutex rw.repo.mMutex, rw.any.mMutex
+// mUnavailableQ mMutex rw.repo.none [0], ro.main.none [5], rw.main.mLoadedMutex
+// mLoadedQ mMutex rw.repo.mLoadedMutex, ro.main.none [5], rw.main.mLoadedMutex
+// mPendingLOD mMutex rw.repo.mPendingMutex, rw.any.mPendingMutex
// mGetMeshCapability mMutex rw.main.mMutex, ro.repo.mMutex (was: [0])
// mGetMesh2Capability mMutex rw.main.mMutex, ro.repo.mMutex (was: [0])
// mGetMeshVersion mMutex rw.main.mMutex, ro.repo.mMutex
@@ -343,20 +343,22 @@ static LLFastTimer::DeclareTimer FTM_MESH_FETCH("Mesh Fetch");
LLMeshRepository gMeshRepo;
-const S32 MESH_HEADER_SIZE = 4096; // Important: assumption is that headers fit in this space
+constexpr U32 CACHE_PREAMBLE_VERSION = 1;
+constexpr S32 CACHE_PREAMBLE_SIZE = sizeof(U32) * 3; //version, header_size, flags
+constexpr S32 MESH_HEADER_SIZE = 4096; // Important: assumption is that headers fit in this space
-const S32 REQUEST2_HIGH_WATER_MIN = 32; // Limits for GetMesh2 regions
-const S32 REQUEST2_HIGH_WATER_MAX = 100;
-const S32 REQUEST2_LOW_WATER_MIN = 16;
-const S32 REQUEST2_LOW_WATER_MAX = 50;
+constexpr S32 REQUEST2_HIGH_WATER_MIN = 32; // Limits for GetMesh2 regions
+constexpr S32 REQUEST2_HIGH_WATER_MAX = 100;
+constexpr S32 REQUEST2_LOW_WATER_MIN = 16;
+constexpr S32 REQUEST2_LOW_WATER_MAX = 50;
-const U32 LARGE_MESH_FETCH_THRESHOLD = 1U << 21; // Size at which requests goes to narrow/slow queue
-const long SMALL_MESH_XFER_TIMEOUT = 120L; // Seconds to complete xfer, small mesh downloads
-const long LARGE_MESH_XFER_TIMEOUT = 600L; // Seconds to complete xfer, large downloads
+constexpr U32 LARGE_MESH_FETCH_THRESHOLD = 1U << 21; // Size at which requests goes to narrow/slow queue
+constexpr long SMALL_MESH_XFER_TIMEOUT = 120L; // Seconds to complete xfer, small mesh downloads
+constexpr long LARGE_MESH_XFER_TIMEOUT = 600L; // Seconds to complete xfer, large downloads
-const U32 DOWNLOAD_RETRY_LIMIT = 8;
-const F32 DOWNLOAD_RETRY_DELAY = 0.5f; // seconds
+constexpr U32 DOWNLOAD_RETRY_LIMIT = 8;
+constexpr F32 DOWNLOAD_RETRY_DELAY = 0.5f; // seconds
// Would normally like to retry on uploads as some
// retryable failures would be recoverable. Unfortunately,
@@ -366,7 +368,7 @@ const F32 DOWNLOAD_RETRY_DELAY = 0.5f; // seconds
// cap which then produces a 404 on retry destroying some
// (occasionally) useful error information. We'll leave
// upload retries to the user as in the past. SH-4667.
-const long UPLOAD_RETRY_LIMIT = 0L;
+constexpr long UPLOAD_RETRY_LIMIT = 0L;
// Maximum mesh version to support. Three least significant digits are reserved for the minor version,
// with major version changes indicating a format change that is not backwards compatible and should not
@@ -374,7 +376,7 @@ const long UPLOAD_RETRY_LIMIT = 0L;
// present, the version is 0.001. A viewer that can parse version 0.001 can also parse versions up to 0.999,
// but not 1.0 (integer 1000).
// See wiki at https://wiki.secondlife.com/wiki/Mesh/Mesh_Asset_Format
-const S32 MAX_MESH_VERSION = 999;
+constexpr S32 MAX_MESH_VERSION = 999;
U32 LLMeshRepository::sBytesReceived = 0;
U32 LLMeshRepository::sMeshRequestCount = 0;
@@ -386,12 +388,12 @@ U32 LLMeshRepository::sLODProcessing = 0;
U32 LLMeshRepository::sLODPending = 0;
U32 LLMeshRepository::sCacheBytesRead = 0;
-U32 LLMeshRepository::sCacheBytesWritten = 0;
+std::atomic<U32> LLMeshRepository::sCacheBytesWritten = 0;
U32 LLMeshRepository::sCacheBytesHeaders = 0;
U32 LLMeshRepository::sCacheBytesSkins = 0;
U32 LLMeshRepository::sCacheBytesDecomps = 0;
U32 LLMeshRepository::sCacheReads = 0;
-U32 LLMeshRepository::sCacheWrites = 0;
+std::atomic<U32> LLMeshRepository::sCacheWrites = 0;
U32 LLMeshRepository::sMaxLockHoldoffs = 0;
LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0, false); // true -> gather cpu metrics
@@ -550,6 +552,7 @@ LLViewerFetchedTexture* LLMeshUploadThread::FindViewerTexture(const LLImportMate
std::atomic<S32> LLMeshRepoThread::sActiveHeaderRequests = 0;
std::atomic<S32> LLMeshRepoThread::sActiveLODRequests = 0;
+std::atomic<S32> LLMeshRepoThread::sActiveSkinRequests = 0;
U32 LLMeshRepoThread::sMaxConcurrentRequests = 1;
S32 LLMeshRepoThread::sRequestLowWater = REQUEST2_LOW_WATER_MIN;
S32 LLMeshRepoThread::sRequestHighWater = REQUEST2_HIGH_WATER_MIN;
@@ -584,6 +587,7 @@ public:
: LLCore::HttpHandler(),
mMeshParams(),
mProcessed(false),
+ mHasDataOwnership(true),
mHttpHandle(LLCORE_HTTP_HANDLE_INVALID),
mOffset(offset),
mRequestedBytes(requested_bytes)
@@ -607,6 +611,9 @@ public:
LLCore::HttpHandle mHttpHandle;
U32 mOffset;
U32 mRequestedBytes;
+
+protected:
+ bool mHasDataOwnership = true;
};
@@ -659,6 +666,9 @@ public:
virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size);
virtual void processFailure(LLCore::HttpStatus status);
+private:
+ void processLod(U8* data, S32 data_size);
+
public:
S32 mLOD;
};
@@ -674,13 +684,17 @@ public:
LLMeshSkinInfoHandler(const LLUUID& id, U32 offset, U32 requested_bytes)
: LLMeshHandlerBase(offset, requested_bytes),
mMeshID(id)
- {}
+ {
+ LLMeshRepoThread::incActiveSkinRequests();
+ }
virtual ~LLMeshSkinInfoHandler();
protected:
LLMeshSkinInfoHandler(const LLMeshSkinInfoHandler &); // Not defined
void operator=(const LLMeshSkinInfoHandler &); // Not defined
+ void processSkin(U8* data, S32 data_size);
+
public:
virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size);
virtual void processFailure(LLCore::HttpStatus status);
@@ -823,6 +837,14 @@ void log_upload_error(LLCore::HttpStatus status, const LLSD& content,
gMeshRepo.uploadError(args);
}
+void write_preamble(LLFileSystem &file, S32 header_bytes, S32 flags)
+{
+ LLMeshRepository::sCacheBytesWritten += CACHE_PREAMBLE_SIZE;
+ file.write((U8*)&CACHE_PREAMBLE_VERSION, sizeof(U32));
+ file.write((U8*)&header_bytes, sizeof(U32));
+ file.write((U8*)&flags, sizeof(U32));
+}
+
LLMeshRepoThread::LLMeshRepoThread()
: LLThread("mesh repo"),
mHttpRequest(NULL),
@@ -837,6 +859,9 @@ LLMeshRepoThread::LLMeshRepoThread()
mMutex = new LLMutex();
mHeaderMutex = new LLMutex();
+ mLoadedMutex = new LLMutex();
+ mPendingMutex = new LLMutex();
+ mSkinMapMutex = new LLMutex();
mSignal = new LLCondition();
mHttpRequest = new LLCore::HttpRequest;
mHttpOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions);
@@ -849,6 +874,11 @@ LLMeshRepoThread::LLMeshRepoThread()
mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_VND_LL_MESH);
mHttpPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_MESH2);
mHttpLargePolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_LARGE_MESH);
+
+ // Lod processing is expensive due to the number of requests
+ // and a need to do expensive cacheOptimize().
+ mMeshThreadPool.reset(new LL::ThreadPool("MeshLodProcessing", 2));
+ mMeshThreadPool->start();
}
@@ -875,13 +905,21 @@ LLMeshRepoThread::~LLMeshRepoThread()
}
delete mHttpRequest;
- mHttpRequest = NULL;
+ mHttpRequest = nullptr;
delete mMutex;
- mMutex = NULL;
+ mMutex = nullptr;
delete mHeaderMutex;
- mHeaderMutex = NULL;
+ mHeaderMutex = nullptr;
+ delete mLoadedMutex;
+ mLoadedMutex = nullptr;
+ delete mPendingMutex;
+ mPendingMutex = nullptr;
+ delete mSkinMapMutex;
+ mSkinMapMutex = nullptr;
delete mSignal;
- mSignal = NULL;
+ mSignal = nullptr;
+ delete[] mDiskCacheBuffer;
+ mDiskCacheBuffer = nullptr;
}
void LLMeshRepoThread::run()
@@ -908,6 +946,7 @@ void LLMeshRepoThread::run()
// On the other hand, this may actually be an effective and efficient scheme...
mSignal->wait();
+ LL_PROFILE_ZONE_NAMED("mesh_thread_loop")
if (LLApp::isExiting())
{
@@ -925,11 +964,55 @@ void LLMeshRepoThread::run()
}
sRequestWaterLevel = static_cast<S32>(mHttpRequestSet.size()); // Stats data update
- // NOTE: order of queue processing intentionally favors LOD requests over header requests
+ // NOTE: order of queue processing intentionally favors LOD and Skin requests over header requests
// Todo: we are processing mLODReqQ, mHeaderReqQ, mSkinRequests, mDecompositionRequests and mPhysicsShapeRequests
// in relatively similar manners, remake code to simplify/unify the process,
// like processRequests(&requestQ, fetchFunction); which does same thing for each element
+ if (mHttpRequestSet.size() < sRequestHighWater
+ && !mSkinRequests.empty())
+ {
+ if (!mSkinRequests.empty())
+ {
+ std::list<UUIDBasedRequest> incomplete;
+ while (!mSkinRequests.empty() && mHttpRequestSet.size() < sRequestHighWater)
+ {
+
+ mMutex->lock();
+ auto req = mSkinRequests.front();
+ mSkinRequests.pop_front();
+ mMutex->unlock();
+ if (req.isDelayed())
+ {
+ incomplete.emplace_back(req);
+ }
+ else if (!fetchMeshSkinInfo(req.mId))
+ {
+ if (req.canRetry())
+ {
+ req.updateTime();
+ incomplete.emplace_back(req);
+ }
+ else
+ {
+ LLMutexLock locker(mLoadedMutex);
+ mSkinUnavailableQ.push_back(req);
+ LL_DEBUGS() << "mSkinReqQ failed: " << req.mId << LL_ENDL;
+ }
+ }
+ }
+
+ if (!incomplete.empty())
+ {
+ LLMutexLock locker(mMutex);
+ for (const auto& req : incomplete)
+ {
+ mSkinRequests.push_back(req);
+ }
+ }
+ }
+ }
+
if (!mLODReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater)
{
std::list<LODRequest> incomplete;
@@ -950,7 +1033,7 @@ void LLMeshRepoThread::run()
// failed to load before, wait a bit
incomplete.push_front(req);
}
- else if (!fetchMeshLOD(req.mMeshParams, req.mLOD, req.canRetry()))
+ else if (!fetchMeshLOD(req.mMeshParams, req.mLOD))
{
if (req.canRetry())
{
@@ -961,7 +1044,7 @@ void LLMeshRepoThread::run()
else
{
// too many fails
- LLMutexLock lock(mMutex);
+ LLMutexLock lock(mLoadedMutex);
mUnavailableQ.push_back(req);
LL_WARNS() << "Failed to load " << req.mMeshParams << " , skip" << LL_ENDL;
}
@@ -998,7 +1081,7 @@ void LLMeshRepoThread::run()
// failed to load before, wait a bit
incomplete.push_front(req);
}
- else if (!fetchMeshHeader(req.mMeshParams, req.canRetry()))
+ else if (!fetchMeshHeader(req.mMeshParams))
{
if (req.canRetry())
{
@@ -1028,54 +1111,13 @@ void LLMeshRepoThread::run()
// performing long-duration actions.
if (mHttpRequestSet.size() < sRequestHighWater
- && (!mSkinRequests.empty()
- || !mDecompositionRequests.empty()
+ && (!mDecompositionRequests.empty()
|| !mPhysicsShapeRequests.empty()))
{
// Something to do probably, lock and double-check. We don't want
// to hold the lock long here. That will stall main thread activities
// so we bounce it.
- if (!mSkinRequests.empty())
- {
- std::list<UUIDBasedRequest> incomplete;
- while (!mSkinRequests.empty() && mHttpRequestSet.size() < sRequestHighWater)
- {
-
- mMutex->lock();
- auto req = mSkinRequests.front();
- mSkinRequests.pop_front();
- mMutex->unlock();
- if (req.isDelayed())
- {
- incomplete.emplace_back(req);
- }
- else if (!fetchMeshSkinInfo(req.mId, req.canRetry()))
- {
- if (req.canRetry())
- {
- req.updateTime();
- incomplete.emplace_back(req);
- }
- else
- {
- LLMutexLock locker(mMutex);
- mSkinUnavailableQ.push_back(req);
- LL_DEBUGS() << "mSkinReqQ failed: " << req.mId << LL_ENDL;
- }
- }
- }
-
- if (!incomplete.empty())
- {
- LLMutexLock locker(mMutex);
- for (const auto& req : incomplete)
- {
- mSkinRequests.push_back(req);
- }
- }
- }
-
// holding lock, try next list
// *TODO: For UI/debug-oriented lists, we might drop the fine-
// grained locking as there's a lowered expectation of smoothness
@@ -1103,7 +1145,7 @@ void LLMeshRepoThread::run()
}
else
{
- LL_DEBUGS() << "mDecompositionRequests failed: " << req.mId << LL_ENDL;
+ LL_DEBUGS(LOG_MESH) << "mDecompositionRequests failed: " << req.mId << LL_ENDL;
}
}
}
@@ -1139,7 +1181,7 @@ void LLMeshRepoThread::run()
}
else
{
- LL_DEBUGS() << "mPhysicsShapeRequests failed: " << req.mId << LL_ENDL;
+ LL_DEBUGS(LOG_MESH) << "mPhysicsShapeRequests failed: " << req.mId << LL_ENDL;
}
}
}
@@ -1195,40 +1237,91 @@ void LLMeshRepoThread::lockAndLoadMeshLOD(const LLVolumeParams& mesh_params, S32
}
}
-
void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
{ //could be called from any thread
const LLUUID& mesh_id = mesh_params.getSculptID();
- LLMutexLock lock(mMutex);
- LLMutexLock header_lock(mHeaderMutex);
- mesh_header_map::iterator iter = mMeshHeader.find(mesh_id);
- if (iter != mMeshHeader.end())
+ loadMeshLOD(mesh_id, mesh_params, lod);
+}
+
+void LLMeshRepoThread::loadMeshLOD(const LLUUID& mesh_id, const LLVolumeParams& mesh_params, S32 lod)
+{
+ if (hasHeader(mesh_id))
{ //if we have the header, request LOD byte range
LODRequest req(mesh_params, lod);
{
+ LLMutexLock lock(mMutex);
mLODReqQ.push(req);
LLMeshRepository::sLODProcessing++;
}
}
else
{
+ LLMutexLock lock(mPendingMutex);
HeaderRequest req(mesh_params);
pending_lod_map::iterator pending = mPendingLOD.find(mesh_id);
if (pending != mPendingLOD.end())
- { //append this lod request to existing header request
- pending->second.push_back(lod);
- llassert(pending->second.size() <= LLModel::NUM_LODS);
+ {
+ //append this lod request to existing header request
+ if (lod < LLModel::NUM_LODS && lod >= 0)
+ {
+ pending->second[lod]++;
+ }
+ else
+ {
+ LL_WARNS(LOG_MESH) << "Invalid LOD request: " << lod << "for mesh" << mesh_id << LL_ENDL;
+ }
+ llassert_msg(lod < LLModel::NUM_LODS, "Requested lod is out of bounds");
}
else
- { //if no header request is pending, fetch header
+ {
+ //if no header request is pending, fetch header
+ auto& array = mPendingLOD[mesh_id];
+ std::fill(array.begin(), array.end(), 0);
+ array[lod]++;
+
+ LLMutexLock lock(mMutex);
mHeaderReqQ.push(req);
- mPendingLOD[mesh_id].push_back(lod);
}
}
}
+U8* LLMeshRepoThread::getDiskCacheBuffer(S32 size)
+{
+ if (mDiskCacheBufferSize < size)
+ {
+ const S32 MINIMUM_BUFFER_SIZE = 8192; // a minimum to avoid frequent early reallocations
+ size = llmax(MINIMUM_BUFFER_SIZE, size);
+ delete[] mDiskCacheBuffer;
+ try
+ {
+ mDiskCacheBuffer = new U8[size];
+ }
+ catch (std::bad_alloc&)
+ {
+ LL_WARNS(LOG_MESH) << "Failed to allocate memory for mesh thread's buffer, size: " << size << LL_ENDL;
+ mDiskCacheBuffer = NULL;
+
+ // Not sure what size is reasonable
+ // but if 30MB allocation failed, we definitely have issues
+ const S32 MAX_SIZE = 30 * 1024 * 1024; //30MB
+ if (size < MAX_SIZE)
+ {
+ LLAppViewer::instance()->outOfMemorySoftQuit();
+ } // else ignore failures for anomalously large data
+ }
+ mDiskCacheBufferSize = size;
+ }
+ else
+ {
+ // reusing old buffer, reset heading bytes to ensure
+ // old content won't be parsable if something fails.
+ memset(mDiskCacheBuffer, 0, 16);
+ }
+ return mDiskCacheBuffer;
+}
+
// Mutex: must be holding mMutex when called
void LLMeshRepoThread::setGetMeshCap(const std::string & mesh_cap)
{
@@ -1327,7 +1420,7 @@ LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url,
}
-bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry)
+bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
{
LL_PROFILE_ZONE_SCOPED;
if (!mHeaderMutex)
@@ -1337,7 +1430,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry)
mHeaderMutex->lock();
- auto header_it = mMeshHeader.find(mesh_id);
+ mesh_header_map::const_iterator header_it = mMeshHeader.find(mesh_id);
if (header_it == mMeshHeader.end())
{ //we have no header info for this mesh, do nothing
mHeaderMutex->unlock();
@@ -1346,23 +1439,24 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry)
++LLMeshRepository::sMeshRequestCount;
bool ret = true;
- U32 header_size = header_it->second.first;
+ const LLMeshHeader& header = header_it->second;
+ U32 header_size = header.mHeaderSize;
if (header_size > 0)
{
- const LLMeshHeader& header = header_it->second.second;
-
S32 version = header.mVersion;
S32 offset = header_size + header.mSkinOffset;
S32 size = header.mSkinSize;
+ bool in_cache = header.mSkinInCache;
mHeaderMutex->unlock();
if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0)
{
//check cache for mesh skin info
+ S32 disk_ofset = offset + CACHE_PREAMBLE_SIZE;
LLFileSystem file(mesh_id, LLAssetType::AT_MESH);
- if (file.getSize() >= offset + size)
+ if (in_cache && file.getSize() >= disk_ofset + size)
{
U8* buffer = new(std::nothrow) U8[size];
if (!buffer)
@@ -1370,19 +1464,19 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry)
LL_WARNS(LOG_MESH) << "Failed to allocate memory for skin info, size: " << size << LL_ENDL;
// Not sure what size is reasonable for skin info,
- // but if 20MB allocation failed, we definetely have issues
+ // but if 30MB allocation failed, we definitely have issues
const S32 MAX_SIZE = 30 * 1024 * 1024; //30MB
if (size < MAX_SIZE)
{
LLAppViewer::instance()->outOfMemorySoftQuit();
} // else ignore failures for anomalously large data
- LLMutexLock locker(mMutex);
+ LLMutexLock locker(mLoadedMutex);
mSkinUnavailableQ.emplace_back(mesh_id);
return true;
}
LLMeshRepository::sCacheBytesRead += size;
++LLMeshRepository::sCacheReads;
- file.seek(offset);
+ file.seek(disk_ofset);
file.read(buffer, size);
//make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written)
@@ -1393,14 +1487,67 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry)
}
if (!zero)
- { //attempt to parse
- if (skinInfoReceived(mesh_id, buffer, size))
+ {
+ //attempt to parse
+ bool posted = mMeshThreadPool->getQueue().post(
+ [mesh_id, buffer, size]
+ ()
+ {
+ if (!gMeshRepo.mThread->skinInfoReceived(mesh_id, buffer, size))
+ {
+ // either header is faulty or something else overwrote the cache
+ S32 header_size = 0;
+ U32 header_flags = 0;
+ {
+ LL_DEBUGS(LOG_MESH) << "Mesh header for ID " << mesh_id << " cache mismatch." << LL_ENDL;
+
+ LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex);
+
+ auto header_it = gMeshRepo.mThread->mMeshHeader.find(mesh_id);
+ if (header_it != gMeshRepo.mThread->mMeshHeader.end())
+ {
+ LLMeshHeader& header = header_it->second;
+ // for safety just mark everything as missing
+ header.mSkinInCache = false;
+ header.mPhysicsConvexInCache = false;
+ header.mPhysicsMeshInCache = false;
+ for (S32 i = 0; i < LLModel::NUM_LODS; ++i)
+ {
+ header.mLodInCache[i] = false;
+ }
+ header_size = header.mHeaderSize;
+ header_flags = header.getFlags();
+ }
+ }
+
+ if (header_size > 0)
+ {
+ LLFileSystem file(mesh_id, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE);
+ if (file.getMaxSize() >= CACHE_PREAMBLE_SIZE)
+ {
+ write_preamble(file, header_size, header_flags);
+ }
+ }
+
+ {
+ LLMutexLock lock(gMeshRepo.mThread->mMutex);
+ UUIDBasedRequest req(mesh_id);
+ gMeshRepo.mThread->mSkinRequests.push_back(req);
+ }
+ }
+ delete[] buffer;
+ });
+ if (posted)
+ {
+ // lambda owns buffer
+ return true;
+ }
+ else if (skinInfoReceived(mesh_id, buffer, size))
{
delete[] buffer;
return true;
}
}
-
delete[] buffer;
}
@@ -1420,26 +1567,21 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry)
<< LL_ENDL;
ret = false;
}
- else if(can_retry)
+ else
{
handler->mHttpHandle = handle;
mHttpRequestSet.insert(handler);
}
- else
- {
- LLMutexLock locker(mMutex);
- mSkinUnavailableQ.emplace_back(mesh_id);
- }
}
else
{
- LLMutexLock locker(mMutex);
+ LLMutexLock locker(mLoadedMutex);
mSkinUnavailableQ.emplace_back(mesh_id);
}
}
else
{
- LLMutexLock locker(mMutex);
+ LLMutexLock locker(mLoadedMutex);
mSkinUnavailableQ.emplace_back(mesh_id);
}
}
@@ -1470,42 +1612,35 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
}
++LLMeshRepository::sMeshRequestCount;
- U32 header_size = header_it->second.first;
+ const auto& header = header_it->second;
+ U32 header_size = header.mHeaderSize;
bool ret = true;
if (header_size > 0)
{
- const auto& header = header_it->second.second;
S32 version = header.mVersion;
S32 offset = header_size + header.mPhysicsConvexOffset;
S32 size = header.mPhysicsConvexSize;
+ bool in_cache = header.mPhysicsConvexInCache;
mHeaderMutex->unlock();
if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0)
{
- //check cache for mesh skin info
+ // check cache for mesh decomposition
+ S32 disk_ofset = offset + CACHE_PREAMBLE_SIZE;
LLFileSystem file(mesh_id, LLAssetType::AT_MESH);
- if (file.getSize() >= offset+size)
+ if (in_cache && file.getSize() >= disk_ofset + size)
{
- U8* buffer = new(std::nothrow) U8[size];
+ U8* buffer = getDiskCacheBuffer(size);
if (!buffer)
{
- LL_WARNS(LOG_MESH) << "Failed to allocate memory for mesh decomposition, size: " << size << LL_ENDL;
-
- // Not sure what size is reasonable for decomposition
- // but if 20MB allocation failed, we definetely have issues
- const S32 MAX_SIZE = 30 * 1024 * 1024; //30MB
- if (size < MAX_SIZE)
- {
- LLAppViewer::instance()->outOfMemorySoftQuit();
- } // else ignore failures for anomalously large decompositiions
return true;
}
LLMeshRepository::sCacheBytesRead += size;
++LLMeshRepository::sCacheReads;
- file.seek(offset);
+ file.seek(disk_ofset);
file.read(buffer, size);
//make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written)
@@ -1519,12 +1654,9 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
{ //attempt to parse
if (decompositionReceived(mesh_id, buffer, size))
{
- delete[] buffer;
return true;
}
}
-
- delete[] buffer;
}
//reading from cache failed for whatever reason, fetch from sim
@@ -1578,41 +1710,36 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
}
++LLMeshRepository::sMeshRequestCount;
- U32 header_size = header_it->second.first;
+ const auto& header = header_it->second;
+ U32 header_size = header.mHeaderSize;
bool ret = true;
if (header_size > 0)
{
- const auto& header = header_it->second.second;
S32 version = header.mVersion;
S32 offset = header_size + header.mPhysicsMeshOffset;
S32 size = header.mPhysicsMeshSize;
+ bool in_cache = header.mPhysicsMeshInCache;
mHeaderMutex->unlock();
+ // todo: check header.mHasPhysicsMesh
if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0)
{
//check cache for mesh physics shape info
+ S32 disk_ofset = offset + CACHE_PREAMBLE_SIZE;
LLFileSystem file(mesh_id, LLAssetType::AT_MESH);
- if (file.getSize() >= offset+size)
+ if (in_cache && file.getSize() >= disk_ofset +size)
{
LLMeshRepository::sCacheBytesRead += size;
++LLMeshRepository::sCacheReads;
- file.seek(offset);
- U8* buffer = new(std::nothrow) U8[size];
+
+ U8* buffer = getDiskCacheBuffer(size);
if (!buffer)
{
- LL_WARNS(LOG_MESH) << "Failed to allocate memory for mesh decomposition, size: " << size << LL_ENDL;
-
- // Not sure what size is reasonable for physcis
- // but if 20MB allocation failed, we definetely have issues
- const S32 MAX_SIZE = 30 * 1024 * 1024; //30MB
- if (size < MAX_SIZE)
- {
- LLAppViewer::instance()->outOfMemorySoftQuit();
- } // else ignore failures for anomalously large data
return true;
}
+ file.seek(disk_ofset);
file.read(buffer, size);
//make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written)
@@ -1626,12 +1753,9 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
{ //attempt to parse
if (physicsShapeReceived(mesh_id, buffer, size) == MESH_OK)
{
- delete[] buffer;
return true;
}
}
-
- delete[] buffer;
}
//reading from cache failed for whatever reason, fetch from sim
@@ -1699,8 +1823,22 @@ void LLMeshRepoThread::decActiveHeaderRequests()
--LLMeshRepoThread::sActiveHeaderRequests;
}
+//static
+void LLMeshRepoThread::incActiveSkinRequests()
+{
+ LLMutexLock lock(gMeshRepo.mThread->mMutex);
+ ++LLMeshRepoThread::sActiveSkinRequests;
+}
+
+//static
+void LLMeshRepoThread::decActiveSkinRequests()
+{
+ LLMutexLock lock(gMeshRepo.mThread->mMutex);
+ --LLMeshRepoThread::sActiveSkinRequests;
+}
+
//return false if failed to get header
-bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool can_retry)
+bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params)
{
LL_PROFILE_ZONE_SCOPED;
++LLMeshRepository::sMeshRequestCount;
@@ -1714,17 +1852,34 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool c
if (size > 0)
{
// *NOTE: if the header size is ever more than 4KB, this will break
- U8 buffer[MESH_HEADER_SIZE];
- S32 bytes = llmin(size, MESH_HEADER_SIZE);
+ constexpr S32 DISK_MINIMAL_READ = 4096;
+ U8 buffer[DISK_MINIMAL_READ * 2];
+ S32 bytes = llmin(size, DISK_MINIMAL_READ);
LLMeshRepository::sCacheBytesRead += bytes;
++LLMeshRepository::sCacheReads;
+
file.read(buffer, bytes);
- if (headerReceived(mesh_params, buffer, bytes) == MESH_OK)
+
+ U32 version = 0;
+ memcpy(&version, buffer, sizeof(U32));
+ if (version == CACHE_PREAMBLE_VERSION)
{
- LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh header for ID " << mesh_params.getSculptID() << " - was retrieved from the cache." << LL_ENDL;
+ S32 header_size = 0;
+ memcpy(&header_size, buffer + sizeof(U32), sizeof(S32));
+ if (header_size + CACHE_PREAMBLE_SIZE > DISK_MINIMAL_READ)
+ {
+ bytes = llmin(size , DISK_MINIMAL_READ * 2);
+ file.read(buffer + DISK_MINIMAL_READ, bytes - DISK_MINIMAL_READ);
+ }
+ U32 flags = 0;
+ memcpy(&flags, buffer + 2 * sizeof(U32), sizeof(U32));
+ if (headerReceived(mesh_params, buffer + CACHE_PREAMBLE_SIZE, bytes - CACHE_PREAMBLE_SIZE, flags) == MESH_OK)
+ {
+ LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh header for ID " << mesh_params.getSculptID() << " - was retrieved from the cache." << LL_ENDL;
- // Found mesh in cache
- return true;
+ // Found mesh in cache
+ return true;
+ }
}
}
}
@@ -1753,7 +1908,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool c
<< LL_ENDL;
retval = false;
}
- else if (can_retry)
+ else
{
handler->mHttpHandle = handle;
mHttpRequestSet.insert(handler);
@@ -1764,7 +1919,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool c
}
//return false if failed to get mesh lod.
-bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, bool can_retry)
+bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
{
LL_PROFILE_ZONE_SCOPED;
if (!mHeaderMutex)
@@ -1784,41 +1939,43 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
++LLMeshRepository::sMeshRequestCount;
bool retval = true;
- U32 header_size = header_it->second.first;
+ const auto& header = header_it->second;
+ U32 header_size = header.mHeaderSize;
if (header_size > 0)
{
- const auto& header = header_it->second.second;
S32 version = header.mVersion;
S32 offset = header_size + header.mLodOffset[lod];
S32 size = header.mLodSize[lod];
+ bool in_cache = header.mLodInCache[lod];
mHeaderMutex->unlock();
if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0)
{
-
+ S32 disk_ofset = offset + CACHE_PREAMBLE_SIZE;
//check cache for mesh asset
LLFileSystem file(mesh_id, LLAssetType::AT_MESH);
- if (file.getSize() >= offset+size)
+ if (in_cache && (file.getSize() >= disk_ofset + size))
{
- U8* buffer = new(std::nothrow) U8[size];
+ U8* buffer = new(std::nothrow) U8[size]; // todo, make buffer thread local and read in thread?
if (!buffer)
{
LL_WARNS(LOG_MESH) << "Can't allocate memory for mesh " << mesh_id << " LOD " << lod << ", size: " << size << LL_ENDL;
// Not sure what size is reasonable for a mesh,
- // but if 20MB allocation failed, we definetely have issues
+ // but if 30MB allocation failed, we definitely have issues
const S32 MAX_SIZE = 30 * 1024 * 1024; //30MB
if (size < MAX_SIZE)
{
LLAppViewer::instance()->outOfMemorySoftQuit();
} // else ignore failures for anomalously large data
- LLMutexLock lock(mMutex);
+
+ LLMutexLock lock(mLoadedMutex);
mUnavailableQ.push_back(LODRequest(mesh_params, lod));
return true;
}
LLMeshRepository::sCacheBytesRead += size;
++LLMeshRepository::sCacheReads;
- file.seek(offset);
+ file.seek(disk_ofset);
file.read(buffer, size);
//make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written)
@@ -1829,15 +1986,76 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
}
if (!zero)
- { //attempt to parse
- if (lodReceived(mesh_params, lod, buffer, size) == MESH_OK)
+ {
+ //attempt to parse
+ const LLVolumeParams params(mesh_params);
+ bool posted = mMeshThreadPool->getQueue().post(
+ [params, mesh_id, lod, buffer, size]
+ ()
{
+ if (gMeshRepo.mThread->lodReceived(params, lod, buffer, size) == MESH_OK)
+ {
+ LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh body for ID " << mesh_id << " - was retrieved from the cache." << LL_ENDL;
+ }
+ else
+ {
+ // either header is faulty or something else overwrote the cache
+ S32 header_size = 0;
+ U32 header_flags = 0;
+ {
+ LL_DEBUGS(LOG_MESH) << "Mesh header for ID " << mesh_id << " cache mismatch." << LL_ENDL;
+
+ LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex);
+
+ auto header_it = gMeshRepo.mThread->mMeshHeader.find(mesh_id);
+ if (header_it != gMeshRepo.mThread->mMeshHeader.end())
+ {
+ LLMeshHeader& header = header_it->second;
+ // for safety just mark everything as missing
+ header.mSkinInCache = false;
+ header.mPhysicsConvexInCache = false;
+ header.mPhysicsMeshInCache = false;
+ for (S32 i = 0; i < LLModel::NUM_LODS; ++i)
+ {
+ header.mLodInCache[i] = false;
+ }
+ header_size = header.mHeaderSize;
+ header_flags = header.getFlags();
+ }
+ }
+
+ if (header_size > 0)
+ {
+ LLFileSystem file(mesh_id, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE);
+ if (file.getMaxSize() >= CACHE_PREAMBLE_SIZE)
+ {
+ write_preamble(file, header_size, header_flags);
+ }
+ }
+
+ {
+ LLMutexLock lock(gMeshRepo.mThread->mMutex);
+ LODRequest req(params, lod);
+ gMeshRepo.mThread->mLODReqQ.push(req);
+ LLMeshRepository::sLODProcessing++;
+ }
+ }
delete[] buffer;
+ });
+ if (posted)
+ {
+ // now lambda owns buffer
+ return true;
+ }
+ else if (lodReceived(mesh_params, lod, buffer, size) == MESH_OK)
+ {
+ delete[] buffer;
LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh body for ID " << mesh_id << " - was retrieved from the cache." << LL_ENDL;
return true;
}
+
}
delete[] buffer;
@@ -1861,27 +2079,22 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
<< LL_ENDL;
retval = false;
}
- else if (can_retry)
+ else
{
+ // we already made a request, store the handle
handler->mHttpHandle = handle;
mHttpRequestSet.insert(handler);
- // *NOTE: Allowing a re-request, not marking as unavailable. Is that correct?
- }
- else
- {
- LLMutexLock lock(mMutex);
- mUnavailableQ.push_back(LODRequest(mesh_params, lod));
}
}
else
{
- LLMutexLock lock(mMutex);
+ LLMutexLock lock(mLoadedMutex);
mUnavailableQ.push_back(LODRequest(mesh_params, lod));
}
}
else
{
- LLMutexLock lock(mMutex);
+ LLMutexLock lock(mLoadedMutex);
mUnavailableQ.push_back(LODRequest(mesh_params, lod));
}
}
@@ -1893,14 +2106,19 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
return retval;
}
-EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size)
+EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size, U32 flags)
{
+ LL_PROFILE_ZONE_SCOPED;
const LLUUID mesh_id = mesh_params.getSculptID();
LLSD header_data;
LLMeshHeader header;
llssize header_size = 0;
+ S32 skin_offset = -1;
+ S32 skin_size = -1;
+ S32 lod_offset[LLModel::NUM_LODS] = { -1 };
+ S32 lod_size[LLModel::NUM_LODS] = { -1 };
if (data_size > 0)
{
llssize dsize = data_size;
@@ -1933,7 +2151,40 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes
// make sure there is at least one lod, function returns -1 and marks as 404 otherwise
else if (LLMeshRepository::getActualMeshLOD(header, 0) >= 0)
{
- header_size += stream.tellg();
+ header.mHeaderSize = (S32)stream.tellg();
+ header_size += header.mHeaderSize;
+ skin_offset = header.mSkinOffset;
+ skin_size = header.mSkinSize;
+
+ memcpy(lod_offset, header.mLodOffset, sizeof(lod_offset));
+ memcpy(lod_size, header.mLodSize, sizeof(lod_size));
+
+ if (flags != 0)
+ {
+ header.setFromFlags(flags);
+ }
+ else
+ {
+ if (header.mSkinSize > 0 && header_size + header.mSkinOffset + header.mSkinSize < data_size)
+ {
+ header.mSkinInCache = true;
+ }
+ if (header.mPhysicsConvexSize > 0 && header_size + header.mPhysicsConvexOffset + header.mPhysicsConvexSize < data_size)
+ {
+ header.mPhysicsConvexInCache = true;
+ }
+ if (header.mPhysicsMeshSize > 0 && header_size + header.mPhysicsMeshOffset + header.mPhysicsMeshSize < data_size)
+ {
+ header.mPhysicsMeshInCache = true;
+ }
+ for (S32 i = 0; i < LLModel::NUM_LODS; ++i)
+ {
+ if (lod_size[i] > 0 && header_size + lod_offset[i] + lod_size[i] < data_size)
+ {
+ header.mLodInCache[i] = true;
+ }
+ }
+ }
}
}
else
@@ -1947,35 +2198,84 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes
{
LLMutexLock lock(mHeaderMutex);
- mMeshHeader[mesh_id] = { (U32)header_size, header };
+ mMeshHeader[mesh_id] = header;
LLMeshRepository::sCacheBytesHeaders += (U32)header_size;
}
// immediately request SkinInfo since we'll need it before we can render any LoD if it is present
+ if (skin_offset >= 0 && skin_size > 0)
{
- LLMutexLock lock(gMeshRepo.mMeshMutex);
+ {
+ LLMutexLock lock(gMeshRepo.mMeshMutex);
- if (gMeshRepo.mLoadingSkins.find(mesh_id) == gMeshRepo.mLoadingSkins.end())
+ if (gMeshRepo.mLoadingSkins.find(mesh_id) == gMeshRepo.mLoadingSkins.end())
+ {
+ gMeshRepo.mLoadingSkins[mesh_id] = {}; // add an empty vector to indicate to main thread that we are loading skin info
+ }
+ }
+
+ S32 offset = (S32)header_size + skin_offset;
+ bool request_skin = true;
+ if (offset + skin_size < data_size)
+ {
+ request_skin = !skinInfoReceived(mesh_id, data + offset, skin_size);
+ }
+ if (request_skin)
{
- gMeshRepo.mLoadingSkins[mesh_id] = {}; // add an empty vector to indicate to main thread that we are loading skin info
+ mSkinRequests.push_back(UUIDBasedRequest(mesh_id));
}
}
- fetchMeshSkinInfo(mesh_id);
-
- LLMutexLock lock(mMutex); // make sure only one thread access mPendingLOD at the same time.
+ std::array<S32, LLModel::NUM_LODS> pending_lods;
+ bool has_pending_lods = false;
+ {
+ LLMutexLock lock(mPendingMutex); // make sure only one thread access mPendingLOD at the same time.
+ pending_lod_map::iterator iter = mPendingLOD.find(mesh_id);
+ if (iter != mPendingLOD.end())
+ {
+ pending_lods = iter->second;
+ mPendingLOD.erase(iter);
+ has_pending_lods = true;
+ }
+ }
//check for pending requests
- pending_lod_map::iterator iter = mPendingLOD.find(mesh_id);
- if (iter != mPendingLOD.end())
+ if (has_pending_lods)
{
- for (U32 i = 0; i < iter->second.size(); ++i)
+ for (S32 i = 0; i < pending_lods.size(); ++i)
{
- LODRequest req(mesh_params, iter->second[i]);
- mLODReqQ.push(req);
- LLMeshRepository::sLODProcessing++;
+ if (pending_lods[i] > 1)
+ {
+ // mLoadingMeshes should be protecting from dupplciates, but looks
+ // like this is possible if object rezzes, unregisterMesh, then
+ // rezzes again before first request completes.
+ // mLoadingMeshes might need to change a bit to not rerequest if
+ // mesh is already pending.
+ //
+ // Todo: Improve mLoadingMeshes and once done turn this into an assert.
+ // Low priority since such situation should be relatively rare
+ LL_INFOS(LOG_MESH) << "Multiple dupplicate requests for mesd ID: " << mesh_id << " LOD: " << i
+ << LL_ENDL;
+ }
+ if (pending_lods[i] > 0 && lod_size[i] > 0)
+ {
+ // try to load from data we just received
+ bool request_lod = true;
+ S32 offset = (S32)header_size + lod_offset[i];
+ if (offset + lod_size[i] <= data_size)
+ {
+ // initial request is 4096 bytes, it's big enough to fit this lod
+ request_lod = lodReceived(mesh_params, i, data + offset, lod_size[i]) != MESH_OK;
+ }
+ if (request_lod)
+ {
+ LLMutexLock lock(mMutex);
+ LODRequest req(mesh_params, i);
+ mLODReqQ.push(req);
+ LLMeshRepository::sLODProcessing++;
+ }
+ }
}
- mPendingLOD.erase(iter);
}
}
@@ -1995,8 +2295,16 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p
if (volume->getNumFaces() > 0)
{
// if we have a valid SkinInfo, cache per-joint bounding boxes for this LOD
- LLMeshSkinInfo* skin_info = mSkinMap[mesh_params.getSculptID()];
- if (skin_info && isAgentAvatarValid())
+ LLPointer<LLMeshSkinInfo> skin_info = nullptr;
+ {
+ LLMutexLock lock(mSkinMapMutex);
+ skin_map::iterator iter = mSkinMap.find(mesh_params.getSculptID());
+ if (iter != mSkinMap.end())
+ {
+ skin_info = iter->second;
+ }
+ }
+ if (skin_info.notNull() && isAgentAvatarValid())
{
for (S32 i = 0; i < volume->getNumFaces(); ++i)
{
@@ -2008,7 +2316,7 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p
LoadedMesh mesh(volume, mesh_params, lod);
{
- LLMutexLock lock(mMutex);
+ LLMutexLock lock(mLoadedMutex);
mLoadedQ.push_back(mesh);
// LLPointer is not thread safe, since we added this pointer into
// threaded list, make sure counter gets decreased inside mutex lock
@@ -2026,6 +2334,7 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p
bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size)
{
+ LL_PROFILE_ZONE_SCOPED;
LLSD skin;
if (data_size > 0)
@@ -2060,12 +2369,15 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat
// copy the skin info for the background thread so we can use it
// to calculate per-joint bounding boxes when volumes are loaded
- mSkinMap[mesh_id] = new LLMeshSkinInfo(*info);
+ {
+ LLMutexLock lock(mSkinMapMutex);
+ mSkinMap[mesh_id] = new LLMeshSkinInfo(*info);
+ }
{
// Move the LLPointer in to the skin info queue to avoid reference
// count modification after we leave the lock
- LLMutexLock lock(mMutex);
+ LLMutexLock lock(mLoadedMutex);
mSkinInfoQ.emplace_back(std::move(info));
}
}
@@ -2075,6 +2387,7 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat
bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S32 data_size)
{
+ LL_PROFILE_ZONE_SCOPED;
LLSD decomp;
if (data_size > 0)
@@ -2101,7 +2414,7 @@ bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S3
LLModel::Decomposition* d = new LLModel::Decomposition(decomp);
d->mMeshID = mesh_id;
{
- LLMutexLock lock(mMutex);
+ LLMutexLock lock(mLoadedMutex);
mDecompositionQ.push_back(d);
}
}
@@ -2111,6 +2424,7 @@ bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S3
EMeshProcessingResult LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 data_size)
{
+ LL_PROFILE_ZONE_SCOPED;
LLSD physics_shape;
LLModel::Decomposition* d = new LLModel::Decomposition();
@@ -2150,7 +2464,7 @@ EMeshProcessingResult LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_
}
{
- LLMutexLock lock(mMutex);
+ LLMutexLock lock(mLoadedMutex);
mDecompositionQ.push_back(d);
}
return MESH_OK;
@@ -2202,7 +2516,6 @@ LLMeshUploadThread::~LLMeshUploadThread()
mHttpRequest = NULL;
delete mMutex;
mMutex = NULL;
-
}
LLMeshUploadThread::DecompRequest::DecompRequest(LLModel* mdl, LLModel* base_model, LLMeshUploadThread* thread)
@@ -2943,15 +3256,17 @@ void LLMeshRepoThread::notifyLoadedMeshes()
return;
}
+ LL_PROFILE_ZONE_SCOPED;
+
if (!mLoadedQ.empty())
{
std::deque<LoadedMesh> loaded_queue;
- mMutex->lock();
+ mLoadedMutex->lock();
if (!mLoadedQ.empty())
{
loaded_queue.swap(mLoadedQ);
- mMutex->unlock();
+ mLoadedMutex->unlock();
update_metrics = true;
@@ -2960,40 +3275,47 @@ void LLMeshRepoThread::notifyLoadedMeshes()
{
if (mesh.mVolume->getNumVolumeFaces() > 0)
{
- gMeshRepo.notifyMeshLoaded(mesh.mMeshParams, mesh.mVolume);
+ gMeshRepo.notifyMeshLoaded(mesh.mMeshParams, mesh.mVolume, mesh.mLOD);
}
else
{
- gMeshRepo.notifyMeshUnavailable(mesh.mMeshParams,
- LLVolumeLODGroup::getVolumeDetailFromScale(mesh.mVolume->getDetail()));
+ gMeshRepo.notifyMeshUnavailable(mesh.mMeshParams, mesh.mLOD, LLVolumeLODGroup::getVolumeDetailFromScale(mesh.mVolume->getDetail()));
}
}
}
+ else
+ {
+ mLoadedMutex->unlock();
+ }
}
if (!mUnavailableQ.empty())
{
std::deque<LODRequest> unavil_queue;
- mMutex->lock();
+ mLoadedMutex->lock();
if (!mUnavailableQ.empty())
{
unavil_queue.swap(mUnavailableQ);
- mMutex->unlock();
+ mLoadedMutex->unlock();
update_metrics = true;
// Process the elements free of the lock
for (const auto& req : unavil_queue)
{
- gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD);
+ gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD, req.mLOD);
}
}
+ else
+ {
+ mLoadedMutex->unlock();
+ }
}
if (!mSkinInfoQ.empty() || !mSkinUnavailableQ.empty() || ! mDecompositionQ.empty())
{
- if (mMutex->trylock())
+ if (mLoadedMutex->trylock())
{
std::deque<LLPointer<LLMeshSkinInfo>> skin_info_q;
std::deque<UUIDBasedRequest> skin_info_unavail_q;
@@ -3014,7 +3336,7 @@ void LLMeshRepoThread::notifyLoadedMeshes()
decomp_q.swap(mDecompositionQ);
}
- mMutex->unlock();
+ mLoadedMutex->unlock();
// Process the elements free of the lock
while (! skin_info_q.empty())
@@ -3051,9 +3373,11 @@ S32 LLMeshRepoThread::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lo
if (iter != mMeshHeader.end())
{
- auto& header = iter->second.second;
-
- return LLMeshRepository::getActualMeshLOD(header, lod);
+ auto& header = iter->second;
+ if (header.mHeaderSize > 0)
+ {
+ return LLMeshRepository::getActualMeshLOD(header, lod);
+ }
}
return lod;
@@ -3220,7 +3544,10 @@ void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespo
processData(body, body_offset, data, static_cast<S32>(data_size) - body_offset);
- delete [] data;
+ if (mHasDataOwnership)
+ {
+ delete [] data;
+ }
}
// Release handler
@@ -3253,7 +3580,7 @@ void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status)
<< LL_ENDL;
// Can't get the header so none of the LODs will be available
- LLMutexLock lock(gMeshRepo.mThread->mMutex);
+ LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);
for (int i(0); i < LLVolumeLODGroup::NUM_LODS; ++i)
{
gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, i));
@@ -3263,6 +3590,7 @@ void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status)
void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */,
U8 * data, S32 data_size)
{
+ LL_PROFILE_ZONE_SCOPED;
LLUUID mesh_id = mMeshParams.getSculptID();
bool success = (!MESH_HEADER_PROCESS_FAILED)
&& ((data != NULL) == (data_size > 0)); // if we have data but no size or have size but no data, something is wrong;
@@ -3282,7 +3610,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b
<< LL_ENDL;
// Can't get the header so none of the LODs will be available
- LLMutexLock lock(gMeshRepo.mThread->mMutex);
+ LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);
for (int i(0); i < LLVolumeLODGroup::NUM_LODS; ++i)
{
gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, i));
@@ -3298,8 +3626,8 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b
LLMeshRepoThread::mesh_header_map::iterator iter = gMeshRepo.mThread->mMeshHeader.find(mesh_id);
if (iter != gMeshRepo.mThread->mMeshHeader.end())
{
- header_bytes = (S32)iter->second.first;
- header = iter->second.second;
+ header = iter->second;
+ header_bytes = header.mHeaderSize;
}
if (header_bytes > 0
@@ -3324,7 +3652,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b
// LLSD is smart and can work like smart pointer, is not thread safe.
gMeshRepo.mThread->mHeaderMutex->unlock();
- S32 bytes = lod_bytes + header_bytes;
+ S32 bytes = lod_bytes + header_bytes + CACHE_PREAMBLE_SIZE;
// It's possible for the remote asset to have more data than is needed for the local cache
@@ -3337,6 +3665,11 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b
LLMeshRepository::sCacheBytesWritten += data_size;
++LLMeshRepository::sCacheWrites;
+ // write preamble
+ U32 flags = header.getFlags();
+ write_preamble(file, header_bytes, flags);
+
+ // write header
file.write(data, data_size);
S32 remaining = bytes - file.tell();
@@ -3359,7 +3692,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b
gMeshRepo.mThread->mHeaderMutex->unlock();
// headerReceived() parsed header, but header's data is invalid so none of the LODs will be available
- LLMutexLock lock(gMeshRepo.mThread->mMutex);
+ LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);
for (int i(0); i < LLVolumeLODGroup::NUM_LODS; ++i)
{
gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, i));
@@ -3388,9 +3721,64 @@ void LLMeshLODHandler::processFailure(LLCore::HttpStatus status)
<< " (" << status.toTerseString() << "). Not retrying."
<< LL_ENDL;
- LLMutexLock lock(gMeshRepo.mThread->mMutex);
+ LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);
gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD));
}
+void LLMeshLODHandler::processLod(U8* data, S32 data_size)
+{
+ EMeshProcessingResult result = gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size);
+ if (result == MESH_OK)
+ {
+ // good fetch from sim, write to cache
+ LLFileSystem file(mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLFileSystem::READ_WRITE);
+
+ S32 offset = mOffset + CACHE_PREAMBLE_SIZE;
+ S32 size = mRequestedBytes;
+
+ if (file.getSize() >= offset + size)
+ {
+ S32 header_bytes = 0;
+ U32 flags = 0;
+ {
+ LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex);
+
+ LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshParams.getSculptID());
+ if (header_it != gMeshRepo.mThread->mMeshHeader.end())
+ {
+ LLMeshHeader& header = header_it->second;
+ // update header
+ if (!header.mLodInCache[mLOD])
+ {
+ header.mLodInCache[mLOD] = true;
+ header_bytes = header.mHeaderSize;
+ flags = header.getFlags();
+ }
+ // todo: handle else because we shouldn't have requested twice?
+ }
+ }
+ if (flags > 0)
+ {
+ write_preamble(file, header_bytes, flags);
+ }
+
+ file.seek(offset, 0);
+ file.write(data, size);
+ LLMeshRepository::sCacheBytesWritten += size;
+ ++LLMeshRepository::sCacheWrites;
+ }
+ }
+ else
+ {
+ LL_WARNS(LOG_MESH) << "Error during mesh LOD processing. ID: " << mMeshParams.getSculptID()
+ << ", Reason: " << result
+ << " LOD: " << mLOD
+ << " Data size: " << data_size
+ << " Not retrying."
+ << LL_ENDL;
+ LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);
+ gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD));
+ }
+}
void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */,
U8 * data, S32 data_size)
@@ -3399,33 +3787,26 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body
if ((!MESH_LOD_PROCESS_FAILED)
&& ((data != NULL) == (data_size > 0))) // if we have data but no size or have size but no data, something is wrong
{
- EMeshProcessingResult result = gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size);
- if (result == MESH_OK)
+ LLMeshHandlerBase::ptr_t shrd_handler = shared_from_this();
+ bool posted = gMeshRepo.mThread->mMeshThreadPool->getQueue().post(
+ [shrd_handler, data, data_size]
+ ()
{
- // good fetch from sim, write to cache
- LLFileSystem file(mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLFileSystem::READ_WRITE);
-
- S32 offset = mOffset;
- S32 size = mRequestedBytes;
+ LLMeshLODHandler* handler = (LLMeshLODHandler * )shrd_handler.get();
+ handler->processLod(data, data_size);
+ delete[] data;
+ });
- if (file.getSize() >= offset+size)
- {
- file.seek(offset);
- file.write(data, size);
- LLMeshRepository::sCacheBytesWritten += size;
- ++LLMeshRepository::sCacheWrites;
- }
+ if (posted)
+ {
+ // ownership of data was passed to the lambda
+ mHasDataOwnership = false;
}
else
{
- LL_WARNS(LOG_MESH) << "Error during mesh LOD processing. ID: " << mMeshParams.getSculptID()
- << ", Reason: " << result
- << " LOD: " << mLOD
- << " Data size: " << data_size
- << " Not retrying."
- << LL_ENDL;
- LLMutexLock lock(gMeshRepo.mThread->mMutex);
- gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD));
+ // mesh thread dies later than event queue, so this is normal
+ LL_INFOS_ONCE(LOG_MESH) << "Failed to post work into mMeshThreadPool" << LL_ENDL;
+ processLod(data, data_size);
}
}
else
@@ -3435,7 +3816,7 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body
<< " LOD: " << mLOD
<< " Data size: " << data_size
<< LL_ENDL;
- LLMutexLock lock(gMeshRepo.mThread->mMutex);
+ LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);
gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD));
}
}
@@ -3446,6 +3827,7 @@ LLMeshSkinInfoHandler::~LLMeshSkinInfoHandler()
{
LL_WARNS(LOG_MESH) << "deleting unprocessed request handler (may be ok on exit)" << LL_ENDL;
}
+ LLMeshRepoThread::decActiveSkinRequests();
}
void LLMeshSkinInfoHandler::processFailure(LLCore::HttpStatus status)
@@ -3454,38 +3836,98 @@ void LLMeshSkinInfoHandler::processFailure(LLCore::HttpStatus status)
<< ", Reason: " << status.toString()
<< " (" << status.toTerseString() << "). Not retrying."
<< LL_ENDL;
- LLMutexLock lock(gMeshRepo.mThread->mMutex);
+ LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);
gMeshRepo.mThread->mSkinUnavailableQ.emplace_back(mMeshID);
}
-void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */,
- U8 * data, S32 data_size)
+void LLMeshSkinInfoHandler::processSkin(U8* data, S32 data_size)
{
- LL_PROFILE_ZONE_SCOPED;
- if ((!MESH_SKIN_INFO_PROCESS_FAILED)
- && ((data != NULL) == (data_size > 0)) // if we have data but no size or have size but no data, something is wrong
- && gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size))
+ if (gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size))
{
// good fetch from sim, write to cache
LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE);
- S32 offset = mOffset;
+ S32 offset = mOffset + CACHE_PREAMBLE_SIZE;
S32 size = mRequestedBytes;
- if (file.getSize() >= offset+size)
+ if (file.getSize() >= offset + size)
{
LLMeshRepository::sCacheBytesWritten += size;
++LLMeshRepository::sCacheWrites;
- file.seek(offset);
+
+ S32 header_bytes = 0;
+ U32 flags = 0;
+ {
+ LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex);
+
+ LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshID);
+ if (header_it != gMeshRepo.mThread->mMeshHeader.end())
+ {
+ LLMeshHeader& header = header_it->second;
+ // update header
+ if (!header.mSkinInCache)
+ {
+ header.mSkinInCache = true;
+ header_bytes = header.mHeaderSize;
+ flags = header.getFlags();
+ }
+ // todo: handle else because we shouldn't have requested twice?
+ }
+ }
+ if (flags > 0)
+ {
+ write_preamble(file, header_bytes, flags);
+ }
+
+ file.seek(offset, 0);
file.write(data, size);
}
}
else
{
LL_WARNS(LOG_MESH) << "Error during mesh skin info processing. ID: " << mMeshID
+ << ", Unknown reason. Not retrying."
+ << LL_ENDL;
+ LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);
+ gMeshRepo.mThread->mSkinUnavailableQ.emplace_back(mMeshID);
+ }
+}
+
+void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */,
+ U8 * data, S32 data_size)
+{
+ LL_PROFILE_ZONE_SCOPED;
+ if ((!MESH_SKIN_INFO_PROCESS_FAILED)
+ && ((data != NULL) == (data_size > 0))) // if we have data but no size or have size but no data, something is wrong
+ {
+ LLMeshHandlerBase::ptr_t shrd_handler = shared_from_this();
+ bool posted = gMeshRepo.mThread->mMeshThreadPool->getQueue().post(
+ [shrd_handler, data, data_size]
+ ()
+ {
+ LLMeshSkinInfoHandler* handler = (LLMeshSkinInfoHandler*)shrd_handler.get();
+ handler->processSkin(data, data_size);
+ delete[] data;
+ });
+
+ if (posted)
+ {
+ // ownership of data was passed to the lambda
+ mHasDataOwnership = false;
+ }
+ else
+ {
+ // mesh thread dies later than event queue, so this is normal
+ LL_INFOS_ONCE(LOG_MESH) << "Failed to post work into mMeshThreadPool" << LL_ENDL;
+ processSkin(data, data_size);
+ }
+ }
+ else
+ {
+ LL_WARNS(LOG_MESH) << "Error during mesh skin info processing. ID: " << mMeshID
<< ", Unknown reason. Not retrying."
<< LL_ENDL;
- LLMutexLock lock(gMeshRepo.mThread->mMutex);
+ LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);
gMeshRepo.mThread->mSkinUnavailableQ.emplace_back(mMeshID);
}
}
@@ -3519,14 +3961,39 @@ void LLMeshDecompositionHandler::processData(LLCore::BufferArray * /* body */, S
// good fetch from sim, write to cache
LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE);
- S32 offset = mOffset;
+ S32 offset = mOffset + CACHE_PREAMBLE_SIZE;
S32 size = mRequestedBytes;
if (file.getSize() >= offset+size)
{
LLMeshRepository::sCacheBytesWritten += size;
++LLMeshRepository::sCacheWrites;
- file.seek(offset);
+
+ S32 header_bytes = 0;
+ U32 flags = 0;
+ {
+ LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex);
+
+ LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshID);
+ if (header_it != gMeshRepo.mThread->mMeshHeader.end())
+ {
+ LLMeshHeader& header = header_it->second;
+ // update header
+ if (!header.mPhysicsConvexInCache)
+ {
+ header.mPhysicsConvexInCache = true;
+ header_bytes = header.mHeaderSize;
+ flags = header.getFlags();
+ }
+ // todo: handle else because we shouldn't have requested twice?
+ }
+ }
+ if (flags > 0)
+ {
+ write_preamble(file, header_bytes, flags);
+ }
+
+ file.seek(offset, 0);
file.write(data, size);
}
}
@@ -3567,14 +4034,39 @@ void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * /* body */, S3
// good fetch from sim, write to cache for caching
LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE);
- S32 offset = mOffset;
+ S32 offset = mOffset + CACHE_PREAMBLE_SIZE;
S32 size = mRequestedBytes;
if (file.getSize() >= offset+size)
{
LLMeshRepository::sCacheBytesWritten += size;
++LLMeshRepository::sCacheWrites;
- file.seek(offset);
+
+ S32 header_bytes = 0;
+ U32 flags = 0;
+ {
+ LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex);
+
+ LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshID);
+ if (header_it != gMeshRepo.mThread->mMeshHeader.end())
+ {
+ LLMeshHeader& header = header_it->second;
+ // update header
+ if (!header.mPhysicsMeshInCache)
+ {
+ header.mPhysicsMeshInCache = true;
+ header_bytes = header.mHeaderSize;
+ flags = header.getFlags();
+ }
+ // todo: handle else because we shouldn't have requested twice?
+ }
+ }
+ if (flags > 0)
+ {
+ write_preamble(file, header_bytes, flags);
+ }
+
+ file.seek(offset, 0);
file.write(data, size);
}
}
@@ -3636,6 +4128,7 @@ void LLMeshRepository::shutdown()
}
mThread->mSignal->broadcast();
+ mThread->mMeshThreadPool->close();
while (!mThread->isStopped())
{
@@ -3710,24 +4203,24 @@ void LLMeshRepository::unregisterMesh(LLVOVolume* vobj)
}
}
-S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 detail, S32 last_lod)
+S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 new_lod, S32 last_lod)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; //LL_LL_RECORD_BLOCK_TIME(FTM_MESH_FETCH);
// Manage time-to-load metrics for mesh download operations.
metricsProgress(1);
- if (detail < 0 || detail >= LLVolumeLODGroup::NUM_LODS)
+ if (new_lod < 0 || new_lod >= LLVolumeLODGroup::NUM_LODS)
{
- return detail;
+ return new_lod;
}
{
LLMutexLock lock(mMeshMutex);
//add volume to list of loading meshes
const auto& mesh_id = mesh_params.getSculptID();
- mesh_load_map::iterator iter = mLoadingMeshes[detail].find(mesh_id);
- if (iter != mLoadingMeshes[detail].end())
+ mesh_load_map::iterator iter = mLoadingMeshes[new_lod].find(mesh_id);
+ if (iter != mLoadingMeshes[new_lod].end())
{ //request pending for this mesh, append volume id to list
auto it = std::find(iter->second.begin(), iter->second.end(), vobj);
if (it == iter->second.end()) {
@@ -3737,8 +4230,8 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para
else
{
//first request for this mesh
- mLoadingMeshes[detail][mesh_id].push_back(vobj);
- mPendingRequests.push_back(LLMeshRepoThread::LODRequest(mesh_params, detail));
+ mLoadingMeshes[new_lod][mesh_id].push_back(vobj);
+ mPendingRequests.emplace_back(new PendingRequestLOD(mesh_params, new_lod));
LLMeshRepository::sLODPending++;
}
}
@@ -3767,7 +4260,7 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para
}
//next, see what the next lowest LOD available might be
- for (S32 i = detail-1; i >= 0; --i)
+ for (S32 i = new_lod -1; i >= 0; --i)
{
LLVolume* lod = group->refLOD(i);
if (lod && lod->isMeshAssetLoaded() && lod->getNumVolumeFaces() > 0)
@@ -3780,7 +4273,7 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para
}
//no lower LOD is a available, is a higher lod available?
- for (S32 i = detail+1; i < LLVolumeLODGroup::NUM_LODS; ++i)
+ for (S32 i = new_lod+1; i < LLVolumeLODGroup::NUM_LODS; ++i)
{
LLVolume* lod = group->refLOD(i);
if (lod && lod->isMeshAssetLoaded() && lod->getNumVolumeFaces() > 0)
@@ -3794,7 +4287,51 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para
}
}
- return detail;
+ return new_lod;
+}
+
+F32 calculate_score(LLVOVolume* object)
+{
+ if (!object)
+ {
+ return -1.f;
+ }
+ LLDrawable* drawable = object->mDrawable;
+ if (!drawable)
+ {
+ return -1;
+ }
+ if (drawable->isState(LLDrawable::RIGGED) || object->isAttachment())
+ {
+ LLVOAvatar* avatar = object->getAvatar();
+ LLDrawable* av_drawable = avatar ? avatar->mDrawable : nullptr;
+ if (avatar && av_drawable)
+ {
+ // See LLVOVolume::calcLOD()
+ F32 radius;
+ if (avatar->isControlAvatar())
+ {
+ const LLVector3* box = avatar->getLastAnimExtents();
+ LLVector3 diag = box[1] - box[0];
+ radius = diag.magVec() * 0.5f;
+ }
+ else
+ {
+ // Volume in a rigged mesh attached to a regular avatar.
+ const LLVector3* box = avatar->getLastAnimExtents();
+ LLVector3 diag = box[1] - box[0];
+ radius = diag.magVec();
+
+ if (!avatar->isSelf() && !avatar->hasFirstFullAttachmentData())
+ {
+ // slightly deprioritize avatars that are still receiving data
+ radius *= 0.9f;
+ }
+ }
+ return radius / llmax(av_drawable->mDistanceWRTCamera, 1.f);
+ }
+ }
+ return drawable->getRadius() / llmax(drawable->mDistanceWRTCamera, 1.f);
}
void LLMeshRepository::notifyLoadedMeshes()
@@ -3918,6 +4455,7 @@ void LLMeshRepository::notifyLoadedMeshes()
// erase from background thread
mThread->mWorkQueue.post([=, this]()
{
+ LLMutexLock(mThread->mSkinMapMutex);
mThread->mSkinMap.erase(id);
});
}
@@ -3974,8 +4512,12 @@ void LLMeshRepository::notifyLoadedMeshes()
mUploadErrorQ.pop();
}
- S32 active_count = LLMeshRepoThread::sActiveHeaderRequests + LLMeshRepoThread::sActiveLODRequests;
- if (active_count < LLMeshRepoThread::sRequestLowWater)
+ // mPendingRequests go into queues, queues go into active http requests.
+ // Checking sRequestHighWater to keep queues at least somewhat populated
+ // for faster transition into http
+ S32 active_count = LLMeshRepoThread::sActiveHeaderRequests + LLMeshRepoThread::sActiveLODRequests + LLMeshRepoThread::sActiveSkinRequests;
+ active_count += (S32)(mThread->mLODReqQ.size() + mThread->mHeaderReqQ.size() + mThread->mSkinInfoQ.size());
+ if (active_count < LLMeshRepoThread::sRequestHighWater)
{
S32 push_count = LLMeshRepoThread::sRequestHighWater - active_count;
@@ -3996,50 +4538,69 @@ void LLMeshRepository::notifyLoadedMeshes()
F32 max_score = 0.f;
for (auto obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter)
{
- LLVOVolume* object = *obj_iter;
- if (object)
+ F32 cur_score = calculate_score(*obj_iter);
+ if (cur_score >= 0.f)
{
- LLDrawable* drawable = object->mDrawable;
- if (drawable)
- {
- F32 cur_score = drawable->getRadius()/llmax(drawable->mDistanceWRTCamera, 1.f);
- max_score = llmax(max_score, cur_score);
- }
+ max_score = llmax(max_score, cur_score);
}
}
score_map[iter->first] = max_score;
}
}
+ for (mesh_load_map::iterator iter = mLoadingSkins.begin(); iter != mLoadingSkins.end(); ++iter)
+ {
+ F32 max_score = 0.f;
+ for (auto obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter)
+ {
+ F32 cur_score = calculate_score(*obj_iter);
+ if (cur_score >= 0.f)
+ {
+ max_score = llmax(max_score, cur_score);
+ }
+ }
+
+ score_map[iter->first] = max_score;
+ }
//set "score" for pending requests
- for (std::vector<LLMeshRepoThread::LODRequest>::iterator iter = mPendingRequests.begin(); iter != mPendingRequests.end(); ++iter)
+ for (std::unique_ptr<PendingRequestBase>& req_p : mPendingRequests)
{
- iter->mScore = score_map[iter->mMeshParams.getSculptID()];
+ req_p->setScore(score_map[req_p->getId()]);
}
//sort by "score"
std::partial_sort(mPendingRequests.begin(), mPendingRequests.begin() + push_count,
- mPendingRequests.end(), LLMeshRepoThread::CompareScoreGreater());
+ mPendingRequests.end(), PendingRequestBase::CompareScoreGreater());
}
-
while (!mPendingRequests.empty() && push_count > 0)
{
- LLMeshRepoThread::LODRequest& request = mPendingRequests.front();
- mThread->loadMeshLOD(request.mMeshParams, request.mLOD);
+ std::unique_ptr<PendingRequestBase>& req_p = mPendingRequests.front();
+ switch (req_p->getRequestType())
+ {
+ case MESH_REQUEST_LOD:
+ {
+ PendingRequestLOD* lod = (PendingRequestLOD*)req_p.get();
+ mThread->loadMeshLOD(lod->mMeshParams, lod->mLOD);
+ LLMeshRepository::sLODPending--;
+ break;
+ }
+ case MESH_REQUEST_SKIN:
+ {
+ PendingRequestUUID* skin = (PendingRequestUUID*)req_p.get();
+ mThread->loadMeshSkinInfo(skin->getId());
+ break;
+ }
+
+ default:
+ LL_ERRS() << "Unknown request type in LLMeshRepository::notifyLoadedMeshes" << LL_ENDL;
+ break;
+ }
mPendingRequests.erase(mPendingRequests.begin());
- LLMeshRepository::sLODPending--;
push_count--;
}
}
- //send skin info requests
- while (!mPendingSkinRequests.empty())
- {
- mThread->loadMeshSkinInfo(mPendingSkinRequests.front());
- mPendingSkinRequests.pop();
- }
-
//send decomposition requests
while (!mPendingDecompositionRequests.empty())
{
@@ -4116,15 +4677,14 @@ void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decom
}
}
-void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume)
+void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume, S32 lod)
{ //called from main thread
- S32 detail = LLVolumeLODGroup::getVolumeDetailFromScale(volume->getDetail());
//get list of objects waiting to be notified this mesh is loaded
const auto& mesh_id = mesh_params.getSculptID();
- mesh_load_map::iterator obj_iter = mLoadingMeshes[detail].find(mesh_id);
+ mesh_load_map::iterator obj_iter = mLoadingMeshes[lod].find(mesh_id);
- if (volume && obj_iter != mLoadingMeshes[detail].end())
+ if (volume && obj_iter != mLoadingMeshes[lod].end())
{
//make sure target volume is still valid
if (volume->getNumVolumeFaces() <= 0)
@@ -4134,6 +4694,7 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol
}
{ //update system volume
+ S32 detail = LLVolumeLODGroup::getVolumeDetailFromScale(volume->getDetail());
LLVolume* sys_volume = LLPrimitive::getVolumeManager()->refVolume(mesh_params, detail);
if (sys_volume)
{
@@ -4157,22 +4718,22 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol
}
}
- mLoadingMeshes[detail].erase(obj_iter);
+ mLoadingMeshes[lod].erase(obj_iter);
LLViewerStatsRecorder::instance().meshLoaded();
}
}
-void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod)
+void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 request_lod, S32 volume_lod)
{ //called from main thread
//get list of objects waiting to be notified this mesh is loaded
const auto& mesh_id = mesh_params.getSculptID();
- mesh_load_map::iterator obj_iter = mLoadingMeshes[lod].find(mesh_id);
- if (obj_iter != mLoadingMeshes[lod].end())
+ mesh_load_map::iterator obj_iter = mLoadingMeshes[request_lod].find(mesh_id);
+ if (obj_iter != mLoadingMeshes[request_lod].end())
{
- F32 detail = LLVolumeLODGroup::getVolumeScaleFromDetail(lod);
+ F32 detail = LLVolumeLODGroup::getVolumeScaleFromDetail(volume_lod);
- LLVolume* sys_volume = LLPrimitive::getVolumeManager()->refVolume(mesh_params, lod);
+ LLVolume* sys_volume = LLPrimitive::getVolumeManager()->refVolume(mesh_params, volume_lod);
if (sys_volume)
{
sys_volume->setMeshAssetUnavaliable(true);
@@ -4189,12 +4750,12 @@ void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params,
obj_volume->getDetail() == detail &&
obj_volume->getParams() == mesh_params)
{ //should force volume to find most appropriate LOD
- vobj->setVolume(obj_volume->getParams(), lod);
+ vobj->setVolume(obj_volume->getParams(), volume_lod);
}
}
}
- mLoadingMeshes[lod].erase(obj_iter);
+ mLoadingMeshes[request_lod].erase(obj_iter);
}
}
@@ -4231,7 +4792,7 @@ const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, LLVOV
{
//first request for this mesh
mLoadingSkins[mesh_id].push_back(requesting_obj);
- mPendingSkinRequests.push(mesh_id);
+ mPendingRequests.emplace_back(new PendingRequestUUID(mesh_id, MESH_REQUEST_SKIN));
}
}
}
@@ -4358,7 +4919,7 @@ bool LLMeshRepository::hasSkinInfo(const LLUUID& mesh_id)
return false;
}
-bool LLMeshRepository::hasHeader(const LLUUID& mesh_id)
+bool LLMeshRepository::hasHeader(const LLUUID& mesh_id) const
{
if (mesh_id.isNull())
{
@@ -4368,13 +4929,13 @@ bool LLMeshRepository::hasHeader(const LLUUID& mesh_id)
return mThread->hasHeader(mesh_id);
}
-bool LLMeshRepoThread::hasPhysicsShapeInHeader(const LLUUID& mesh_id)
+bool LLMeshRepoThread::hasPhysicsShapeInHeader(const LLUUID& mesh_id) const
{
LLMutexLock lock(mHeaderMutex);
- mesh_header_map::iterator iter = mMeshHeader.find(mesh_id);
- if (iter != mMeshHeader.end() && iter->second.first > 0)
+ mesh_header_map::const_iterator iter = mMeshHeader.find(mesh_id);
+ if (iter != mMeshHeader.end() && iter->second.mHeaderSize > 0)
{
- LLMeshHeader &mesh = iter->second.second;
+ const LLMeshHeader &mesh = iter->second;
if (mesh.mPhysicsMeshSize > 0)
{
return true;
@@ -4384,13 +4945,13 @@ bool LLMeshRepoThread::hasPhysicsShapeInHeader(const LLUUID& mesh_id)
return false;
}
-bool LLMeshRepoThread::hasSkinInfoInHeader(const LLUUID& mesh_id)
+bool LLMeshRepoThread::hasSkinInfoInHeader(const LLUUID& mesh_id) const
{
LLMutexLock lock(mHeaderMutex);
- mesh_header_map::iterator iter = mMeshHeader.find(mesh_id);
- if (iter != mMeshHeader.end() && iter->second.first > 0)
+ mesh_header_map::const_iterator iter = mMeshHeader.find(mesh_id);
+ if (iter != mMeshHeader.end() && iter->second.mHeaderSize > 0)
{
- LLMeshHeader& mesh = iter->second.second;
+ const LLMeshHeader& mesh = iter->second;
if (mesh.mSkinOffset >= 0
&& mesh.mSkinSize > 0)
{
@@ -4401,10 +4962,10 @@ bool LLMeshRepoThread::hasSkinInfoInHeader(const LLUUID& mesh_id)
return false;
}
-bool LLMeshRepoThread::hasHeader(const LLUUID& mesh_id)
+bool LLMeshRepoThread::hasHeader(const LLUUID& mesh_id) const
{
LLMutexLock lock(mHeaderMutex);
- mesh_header_map::iterator iter = mMeshHeader.find(mesh_id);
+ mesh_header_map::const_iterator iter = mMeshHeader.find(mesh_id);
return iter != mMeshHeader.end();
}
@@ -4419,16 +4980,16 @@ void LLMeshRepository::uploadModel(std::vector<LLModelInstance>& data, LLVector3
mUploadWaitList.push_back(thread);
}
-S32 LLMeshRepository::getMeshSize(const LLUUID& mesh_id, S32 lod)
+S32 LLMeshRepository::getMeshSize(const LLUUID& mesh_id, S32 lod) const
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME;
if (mThread && mesh_id.notNull() && LLPrimitive::NO_LOD != lod)
{
LLMutexLock lock(mThread->mHeaderMutex);
- LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id);
- if (iter != mThread->mMeshHeader.end() && iter->second.first > 0)
+ LLMeshRepoThread::mesh_header_map::const_iterator iter = mThread->mMeshHeader.find(mesh_id);
+ if (iter != mThread->mMeshHeader.end() && iter->second.mHeaderSize > 0)
{
- const LLMeshHeader& header = iter->second.second;
+ const LLMeshHeader& header = iter->second;
if (header.m404)
{
@@ -4532,9 +5093,9 @@ F32 LLMeshRepository::getStreamingCostLegacy(LLUUID mesh_id, F32 radius, S32* by
{
LLMutexLock lock(mThread->mHeaderMutex);
LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id);
- if (iter != mThread->mMeshHeader.end() && iter->second.first > 0)
+ if (iter != mThread->mMeshHeader.end() && iter->second.mHeaderSize > 0)
{
- result = getStreamingCostLegacy(iter->second.second, radius, bytes, bytes_visible, lod, unscaled_value);
+ result = getStreamingCostLegacy(iter->second, radius, bytes, bytes_visible, lod, unscaled_value);
}
}
if (result > 0.f)
@@ -4726,7 +5287,7 @@ bool LLMeshCostData::init(const LLMeshHeader& header)
}
-S32 LLMeshCostData::getSizeByLOD(S32 lod)
+S32 LLMeshCostData::getSizeByLOD(S32 lod) const
{
if (llclamp(lod,0,3) != lod)
{
@@ -4735,12 +5296,12 @@ S32 LLMeshCostData::getSizeByLOD(S32 lod)
return mSizeByLOD[lod];
}
-S32 LLMeshCostData::getSizeTotal()
+S32 LLMeshCostData::getSizeTotal() const
{
return mSizeByLOD[0] + mSizeByLOD[1] + mSizeByLOD[2] + mSizeByLOD[3];
}
-F32 LLMeshCostData::getEstTrisByLOD(S32 lod)
+F32 LLMeshCostData::getEstTrisByLOD(S32 lod) const
{
if (llclamp(lod,0,3) != lod)
{
@@ -4749,12 +5310,12 @@ F32 LLMeshCostData::getEstTrisByLOD(S32 lod)
return mEstTrisByLOD[lod];
}
-F32 LLMeshCostData::getEstTrisMax()
+F32 LLMeshCostData::getEstTrisMax() const
{
return llmax(mEstTrisByLOD[0], mEstTrisByLOD[1], mEstTrisByLOD[2], mEstTrisByLOD[3]);
}
-F32 LLMeshCostData::getRadiusWeightedTris(F32 radius)
+F32 LLMeshCostData::getRadiusWeightedTris(F32 radius) const
{
F32 max_distance = 512.f;
@@ -4798,7 +5359,7 @@ F32 LLMeshCostData::getRadiusWeightedTris(F32 radius)
return weighted_avg;
}
-F32 LLMeshCostData::getEstTrisForStreamingCost()
+F32 LLMeshCostData::getEstTrisForStreamingCost() const
{
LL_DEBUGS("StreamingCost") << "tris_by_lod: "
<< mEstTrisByLOD[0] << ", "
@@ -4808,7 +5369,7 @@ F32 LLMeshCostData::getEstTrisForStreamingCost()
F32 charged_tris = mEstTrisByLOD[3];
F32 allowed_tris = mEstTrisByLOD[3];
- const F32 ENFORCE_FLOOR = 64.0f;
+ constexpr F32 ENFORCE_FLOOR = 64.0f;
for (S32 i=2; i>=0; i--)
{
// How many tris can we have in this LOD without affecting land impact?
@@ -4825,13 +5386,13 @@ F32 LLMeshCostData::getEstTrisForStreamingCost()
return charged_tris;
}
-F32 LLMeshCostData::getRadiusBasedStreamingCost(F32 radius)
+F32 LLMeshCostData::getRadiusBasedStreamingCost(F32 radius) const
{
static LLCachedControl<U32> mesh_triangle_budget(gSavedSettings, "MeshTriangleBudget");
return getRadiusWeightedTris(radius)/mesh_triangle_budget*15000.f;
}
-F32 LLMeshCostData::getTriangleBasedStreamingCost()
+F32 LLMeshCostData::getTriangleBasedStreamingCost() const
{
F32 result = ANIMATED_OBJECT_COST_PER_KTRI * 0.001f * getEstTrisForStreamingCost();
return result;
@@ -4846,9 +5407,9 @@ bool LLMeshRepository::getCostData(LLUUID mesh_id, LLMeshCostData& data)
{
LLMutexLock lock(mThread->mHeaderMutex);
LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id);
- if (iter != mThread->mMeshHeader.end() && iter->second.first > 0)
+ if (iter != mThread->mMeshHeader.end() && iter->second.mHeaderSize > 0)
{
- LLMeshHeader& header = iter->second.second;
+ LLMeshHeader& header = iter->second;
bool header_invalid = (header.m404
|| header.mLodSize[0] <= 0
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index 3d25a4dd78..0d9da32e27 100644
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -63,6 +63,16 @@ typedef enum e_mesh_processing_result_enum
MESH_UNKNOWN
} EMeshProcessingResult;
+typedef enum e_mesh_request_type_enum
+{
+ MESH_REQUEST_HEADER,
+ MESH_REQUEST_LOD,
+ MESH_REQUEST_SKIN,
+ MESH_REQUEST_DECOMPOSITION,
+ MESH_REQUEST_PHYSICS,
+ MESH_REQUEST_UKNOWN
+} EMeshRequestType;
+
class LLMeshUploadData
{
public:
@@ -183,7 +193,8 @@ public:
class RequestStats
{
public:
- RequestStats() : mRetries(0) {};
+
+ RequestStats() :mRetries(0) {};
void updateTime();
bool canRetry() const;
@@ -195,6 +206,67 @@ private:
LLFrameTimer mTimer;
};
+
+class PendingRequestBase
+{
+public:
+ struct CompareScoreGreater
+ {
+ bool operator()(const std::unique_ptr<PendingRequestBase>& lhs, const std::unique_ptr<PendingRequestBase>& rhs)
+ {
+ return lhs->mScore > rhs->mScore; // greatest = first
+ }
+ };
+
+ PendingRequestBase() : mScore(0.f) {};
+ virtual ~PendingRequestBase() {}
+
+ bool operator<(const PendingRequestBase& rhs) const
+ {
+ return mId < rhs.mId;
+ }
+
+ void setScore(F32 score) { mScore = score; }
+ F32 getScore() const { return mScore; }
+ LLUUID getId() const { return mId; }
+ virtual EMeshRequestType getRequestType() const = 0;
+
+protected:
+ F32 mScore;
+ LLUUID mId;
+};
+
+class PendingRequestLOD : public PendingRequestBase
+{
+public:
+ LLVolumeParams mMeshParams;
+ S32 mLOD;
+
+ PendingRequestLOD(const LLVolumeParams& mesh_params, S32 lod)
+ : PendingRequestBase(), mMeshParams(mesh_params), mLOD(lod)
+ {
+ mId = mMeshParams.getSculptID();
+ }
+
+ EMeshRequestType getRequestType() const override { return MESH_REQUEST_LOD; }
+};
+
+class PendingRequestUUID : public PendingRequestBase
+{
+public:
+
+ PendingRequestUUID(const LLUUID& id, EMeshRequestType type)
+ : PendingRequestBase(), mRequestType(type)
+ {
+ mId = id;
+ }
+
+ EMeshRequestType getRequestType() const override { return mRequestType; }
+
+private:
+ EMeshRequestType mRequestType;
+};
+
class LLMeshHeader
{
public:
@@ -235,19 +307,67 @@ public:
m404 = header.has("404");
}
+private:
+
+ enum EDiskCacheFlags {
+ FLAG_SKIN = 1 << LLModel::NUM_LODS,
+ FLAG_PHYSCONVEX = 1 << (LLModel::NUM_LODS + 1),
+ FLAG_PHYSMESH = 1 << (LLModel::NUM_LODS + 2),
+ };
+public:
+ U32 getFlags()
+ {
+ U32 flags = 0;
+ for (U32 i = 0; i < LLModel::NUM_LODS; i++)
+ {
+ if (mLodInCache[i])
+ {
+ flags |= 1 << i;
+ }
+ }
+ if (mSkinInCache)
+ {
+ flags |= FLAG_SKIN;
+ }
+ if (mPhysicsConvexInCache)
+ {
+ flags |= FLAG_PHYSCONVEX;
+ }
+ if (mPhysicsMeshInCache)
+ {
+ flags |= FLAG_PHYSMESH;
+ }
+ return flags;
+ }
+
+ void setFromFlags(U32 flags)
+ {
+ for (U32 i = 0; i < LLModel::NUM_LODS; i++)
+ {
+ mLodInCache[i] = (flags & (1 << i)) != 0;
+ }
+ mSkinInCache = (flags & FLAG_SKIN) != 0;
+ mPhysicsConvexInCache = (flags & FLAG_PHYSCONVEX) != 0;
+ mPhysicsMeshInCache = (flags & FLAG_PHYSMESH) != 0;
+ }
S32 mVersion = -1;
S32 mSkinOffset = -1;
S32 mSkinSize = -1;
+ bool mSkinInCache = false;
S32 mPhysicsConvexOffset = -1;
S32 mPhysicsConvexSize = -1;
+ bool mPhysicsConvexInCache = false;
S32 mPhysicsMeshOffset = -1;
S32 mPhysicsMeshSize = -1;
+ bool mPhysicsMeshInCache = false;
- S32 mLodOffset[4] = { -1 };
- S32 mLodSize[4] = { -1 };
+ S32 mLodOffset[LLModel::NUM_LODS] = { -1 };
+ S32 mLodSize[LLModel::NUM_LODS] = { -1 };
+ bool mLodInCache[LLModel::NUM_LODS] = { false };
+ S32 mHeaderSize = -1;
bool m404 = false;
};
@@ -258,6 +378,7 @@ public:
static std::atomic<S32> sActiveHeaderRequests;
static std::atomic<S32> sActiveLODRequests;
+ static std::atomic<S32> sActiveSkinRequests;
static U32 sMaxConcurrentRequests;
static S32 sRequestLowWater;
static S32 sRequestHighWater;
@@ -265,10 +386,13 @@ public:
LLMutex* mMutex;
LLMutex* mHeaderMutex;
+ LLMutex* mLoadedMutex;
+ LLMutex* mPendingMutex;
+ LLMutex* mSkinMapMutex;
LLCondition* mSignal;
//map of known mesh headers
- typedef boost::unordered_map<LLUUID, std::pair<U32, LLMeshHeader>> mesh_header_map; // pair is header_size and data
+ typedef boost::unordered_map<LLUUID, LLMeshHeader> mesh_header_map; // pair is header_size and data
mesh_header_map mMeshHeader;
class HeaderRequest : public RequestStats
@@ -292,19 +416,10 @@ public:
public:
LLVolumeParams mMeshParams;
S32 mLOD;
- F32 mScore;
LODRequest(const LLVolumeParams& mesh_params, S32 lod)
- : RequestStats(), mMeshParams(mesh_params), mLOD(lod), mScore(0.f)
- {
- }
- };
-
- struct CompareScoreGreater
- {
- bool operator()(const LODRequest& lhs, const LODRequest& rhs)
+ : RequestStats(), mMeshParams(mesh_params), mLOD(lod)
{
- return lhs.mScore > rhs.mScore; // greatest = first
}
};
@@ -369,7 +484,7 @@ public:
std::deque<LoadedMesh> mLoadedQ;
//map of pending header requests and currently desired LODs
- typedef std::unordered_map<LLUUID, std::vector<S32> > pending_lod_map;
+ typedef std::unordered_map<LLUUID, std::array<S32, LLModel::NUM_LODS> > pending_lod_map;
pending_lod_map mPendingLOD;
// map of mesh ID to skin info (mirrors LLMeshRepository::mSkinMap)
@@ -379,6 +494,8 @@ public:
// workqueue for processing generic requests
LL::WorkQueue mWorkQueue;
+ // lods have their own thread due to costly cacheOptimize() calls
+ std::unique_ptr<LL::ThreadPool> mMeshThreadPool;
// llcorehttp library interface objects.
LLCore::HttpStatus mHttpStatus;
@@ -402,16 +519,16 @@ public:
void lockAndLoadMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
void loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
- bool fetchMeshHeader(const LLVolumeParams& mesh_params, bool can_retry = true);
- bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, bool can_retry = true);
- EMeshProcessingResult headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size);
+ bool fetchMeshHeader(const LLVolumeParams& mesh_params);
+ bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
+ EMeshProcessingResult headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size, U32 flags = 0);
EMeshProcessingResult lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size);
bool skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size);
bool decompositionReceived(const LLUUID& mesh_id, U8* data, S32 data_size);
EMeshProcessingResult physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 data_size);
- bool hasPhysicsShapeInHeader(const LLUUID& mesh_id);
- bool hasSkinInfoInHeader(const LLUUID& mesh_id);
- bool hasHeader(const LLUUID& mesh_id);
+ bool hasPhysicsShapeInHeader(const LLUUID& mesh_id) const;
+ bool hasSkinInfoInHeader(const LLUUID& mesh_id) const;
+ bool hasHeader(const LLUUID& mesh_id) const;
void notifyLoadedMeshes();
S32 getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
@@ -422,7 +539,7 @@ public:
//send request for skin info, returns true if header info exists
// (should hold onto mesh_id and try again later if header info does not exist)
- bool fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry = true);
+ bool fetchMeshSkinInfo(const LLUUID& mesh_id);
//send request for decomposition, returns true if header info exists
// (should hold onto mesh_id and try again later if header info does not exist)
@@ -436,6 +553,8 @@ public:
static void decActiveLODRequests();
static void incActiveHeaderRequests();
static void decActiveHeaderRequests();
+ static void incActiveSkinRequests();
+ static void decActiveSkinRequests();
// Set the caps strings and preferred version for constructing
// mesh fetch URLs.
@@ -456,6 +575,14 @@ private:
LLCore::HttpHandle getByteRange(const std::string & url,
size_t offset, size_t len,
const LLCore::HttpHandler::ptr_t &handler);
+
+ // Mutex: acquires mPendingMutex, mMutex and mHeaderMutex as needed
+ void loadMeshLOD(const LLUUID &mesh_id, const LLVolumeParams& mesh_params, S32 lod);
+
+ // Threads: Repo thread only
+ U8* getDiskCacheBuffer(S32 size);
+ S32 mDiskCacheBufferSize = 0;
+ U8* mDiskCacheBuffer = nullptr;
};
@@ -568,35 +695,35 @@ public:
bool init(const LLMeshHeader& header);
// Size for given LOD
- S32 getSizeByLOD(S32 lod);
+ S32 getSizeByLOD(S32 lod) const;
// Sum of all LOD sizes.
- S32 getSizeTotal();
+ S32 getSizeTotal() const;
// Estimated triangle counts for the given LOD.
- F32 getEstTrisByLOD(S32 lod);
+ F32 getEstTrisByLOD(S32 lod) const;
// Estimated triangle counts for the largest LOD. Typically this
// is also the "high" LOD, but not necessarily.
- F32 getEstTrisMax();
+ F32 getEstTrisMax() const;
// Triangle count as computed by original streaming cost
// formula. Triangles in each LOD are weighted based on how
// frequently they will be seen.
// This was called "unscaled_value" in the original getStreamingCost() functions.
- F32 getRadiusWeightedTris(F32 radius);
+ F32 getRadiusWeightedTris(F32 radius) const;
// Triangle count used by triangle-based cost formula. Based on
// triangles in highest LOD plus potentially partial charges for
// lower LODs depending on complexity.
- F32 getEstTrisForStreamingCost();
+ F32 getEstTrisForStreamingCost() const;
// Streaming cost. This should match the server-side calculation
// for the corresponding volume.
- F32 getRadiusBasedStreamingCost(F32 radius);
+ F32 getRadiusBasedStreamingCost(F32 radius) const;
// New streaming cost formula, currently only used for animated objects.
- F32 getTriangleBasedStreamingCost();
+ F32 getTriangleBasedStreamingCost() const;
private:
// From the "size" field of the mesh header. LOD 0=lowest, 3=highest.
@@ -620,12 +747,12 @@ public:
static U32 sLODPending;
static U32 sLODProcessing;
static U32 sCacheBytesRead;
- static U32 sCacheBytesWritten;
+ static std::atomic<U32> sCacheBytesWritten;
static U32 sCacheBytesHeaders;
static U32 sCacheBytesSkins;
static U32 sCacheBytesDecomps;
static U32 sCacheReads;
- static U32 sCacheWrites;
+ static std::atomic<U32> sCacheWrites;
static U32 sMaxLockHoldoffs; // Maximum sequential locking failures
static LLDeadmanTimer sQuiescentTimer; // Time-to-complete-mesh-downloads after significant events
@@ -646,11 +773,11 @@ public:
void unregisterMesh(LLVOVolume* volume);
//mesh management functions
- S32 loadMesh(LLVOVolume* volume, const LLVolumeParams& mesh_params, S32 detail = 0, S32 last_lod = -1);
+ S32 loadMesh(LLVOVolume* volume, const LLVolumeParams& mesh_params, S32 new_lod = 0, S32 last_lod = -1);
void notifyLoadedMeshes();
- void notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume);
- void notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod);
+ void notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume, S32 lod);
+ void notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 request_lod, S32 volume_lod);
void notifySkinInfoReceived(LLMeshSkinInfo* info);
void notifySkinInfoUnavailable(const LLUUID& info);
void notifyDecompositionReceived(LLModel::Decomposition* info);
@@ -662,7 +789,7 @@ public:
void fetchPhysicsShape(const LLUUID& mesh_id);
bool hasPhysicsShape(const LLUUID& mesh_id);
bool hasSkinInfo(const LLUUID& mesh_id);
- bool hasHeader(const LLUUID& mesh_id);
+ bool hasHeader(const LLUUID& mesh_id) const;
void buildHull(const LLVolumeParams& params, S32 detail);
void buildPhysicsMesh(LLModel::Decomposition& decomp);
@@ -676,7 +803,7 @@ public:
LLHandle<LLWholeModelFeeObserver> fee_observer= (LLHandle<LLWholeModelFeeObserver>()),
LLHandle<LLWholeModelUploadObserver> upload_observer = (LLHandle<LLWholeModelUploadObserver>()));
- S32 getMeshSize(const LLUUID& mesh_id, S32 lod);
+ S32 getMeshSize(const LLUUID& mesh_id, S32 lod) const;
// Quiescent timer management, main thread only.
static void metricsStart();
@@ -684,7 +811,7 @@ public:
static void metricsProgress(unsigned int count);
static void metricsUpdate();
- typedef boost::unordered_map<LLUUID, std::vector<LLVOVolume*> > mesh_load_map;
+ typedef std::unordered_map<LLUUID, std::vector<LLVOVolume*> > mesh_load_map;
mesh_load_map mLoadingMeshes[4];
typedef std::unordered_map<LLUUID, LLPointer<LLMeshSkinInfo>> skin_map;
@@ -695,15 +822,13 @@ public:
LLMutex* mMeshMutex;
- std::vector<LLMeshRepoThread::LODRequest> mPendingRequests;
+ typedef std::vector <std::unique_ptr<PendingRequestBase> > pending_requests_vec;
+ pending_requests_vec mPendingRequests;
//list of mesh ids awaiting skin info
- typedef boost::unordered_map<LLUUID, std::vector<LLVOVolume*> > skin_load_map;
+ typedef std::unordered_map<LLUUID, std::vector<LLVOVolume*> > skin_load_map;
skin_load_map mLoadingSkins;
- //list of mesh ids that need to send skin info fetch requests
- std::queue<LLUUID> mPendingSkinRequests;
-
//list of mesh ids awaiting decompositions
std::unordered_set<LLUUID> mLoadingDecompositions;
diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp
index 74fb2f0f93..cc2c7f2a99 100644
--- a/indra/newview/llpanelface.cpp
+++ b/indra/newview/llpanelface.cpp
@@ -420,6 +420,7 @@ bool LLPanelFace::postBuild()
mCtrlColorTransp->setFollowsLeft();
getChildSetCommitCallback(mCheckFullbright, "checkbox fullbright", [&](LLUICtrl*, const LLSD&) { onCommitFullbright(); });
+ getChildSetCommitCallback(mCheckHideWater, "checkbox_hide_water", [&](LLUICtrl*, const LLSD&) { onCommitHideWater(); });
mLabelTexGen = getChild<LLTextBox>("tex gen");
getChildSetCommitCallback(mComboTexGen, "combobox texgen", [&](LLUICtrl*, const LLSD&) { onCommitTexGen(); });
@@ -1024,6 +1025,13 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)
LLSelectedTEMaterial::getNormalID(normmap_id, identical_norm);
LLSelectedTEMaterial::getSpecularID(specmap_id, identical_spec);
+ LLColor4 color = LLColor4::white;
+ bool identical_color = false;
+
+ LLSelectedTE::getColor(color, identical_color);
+ F32 transparency = (1.f - color.mV[VALPHA]) * 100.f;
+ mExcludeWater = (id == IMG_ALPHA_GRAD) && normmap_id.isNull() && specmap_id.isNull() && (transparency == 0);
+
static S32 selected_te = -1;
static LLUUID prev_obj_id;
if ((LLToolFace::getInstance() == LLToolMgr::getInstance()->getCurrentTool()) &&
@@ -1098,12 +1106,26 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)
updateVisibility(objectp);
+ // Water exclusion
+ {
+ mCheckHideWater->setEnabled(editable && !has_pbr_material && !isMediaTexSelected());
+ mCheckHideWater->set(mExcludeWater);
+ if (mExcludeWater && !has_pbr_material)
+ {
+ mComboMatMedia->selectNthItem(MATMEDIA_MATERIAL);
+ }
+ editable &= !mExcludeWater;
+
+ // disable controls for water exclusion face after updateVisibility, so the whole panel is not hidden
+ mComboMatMedia->setEnabled(editable);
+ mRadioMaterialType->setEnabled(editable);
+ mRadioPbrType->setEnabled(editable);
+ mCheckSyncSettings->setEnabled(editable);
+ }
+
// Color swatch
mLabelColor->setEnabled(editable);
- LLColor4 color = LLColor4::white;
- bool identical_color = false;
- LLSelectedTE::getColor(color, identical_color);
LLColor4 prev_color = mColorSwatch->get();
mColorSwatch->setOriginal(color);
mColorSwatch->set(color, force_set_values || (prev_color != color) || !editable);
@@ -1114,7 +1136,6 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)
// Color transparency
mLabelColorTransp->setEnabled(editable);
- F32 transparency = (1.f - color.mV[VALPHA]) * 100.f;
mCtrlColorTransp->setValue(editable ? transparency : 0);
mCtrlColorTransp->setEnabled(editable && has_material);
@@ -1986,7 +2007,8 @@ void LLPanelFace::updateCopyTexButton()
mMenuClipboardTexture->setEnabled(objectp && objectp->getPCode() == LL_PCODE_VOLUME && objectp->permModify()
&& !objectp->isPermanentEnforced() && !objectp->isInventoryPending()
&& (LLSelectMgr::getInstance()->getSelection()->getObjectCount() == 1)
- && LLMaterialEditor::canClipboardObjectsMaterial());
+ && LLMaterialEditor::canClipboardObjectsMaterial()
+ && !mExcludeWater);
std::string tooltip = (objectp && objectp->isInventoryPending()) ? LLTrans::getString("LoadingContents") : getString("paste_options");
mMenuClipboardTexture->setToolTip(tooltip);
}
@@ -3027,6 +3049,37 @@ void LLPanelFace::onCommitFullbright()
sendFullbright();
}
+void LLPanelFace::onCommitHideWater()
+{
+ if (mCheckHideWater->get())
+ {
+ LLHandle<LLPanel> handle = getHandle();
+ LLNotificationsUtil::add("WaterExclusionSurfacesWarning", LLSD(), LLSD(),
+ [handle](const LLSD& notification, const LLSD& response)
+ {
+ if(LLPanelFace* panel = (LLPanelFace*)handle.get())
+ {
+ if (LLNotificationsUtil::getSelectedOption(notification, response) == 1)
+ {
+ panel->mCheckHideWater->setValue(false);
+ return;
+ }
+ // apply invisiprim texture and reset related params to set water exclusion surface
+ panel->sendBump(0);
+ panel->sendShiny(0);
+ LLSelectMgr::getInstance()->selectionSetAlphaOnly(1.f);
+ LLSelectMgr::getInstance()->selectionSetImage(IMG_ALPHA_GRAD);
+ LLSelectedTEMaterial::setDiffuseAlphaMode(panel, LLMaterial::DIFFUSE_ALPHA_MODE_BLEND);
+ }
+ });
+ }
+ else
+ {
+ // reset texture to default plywood
+ LLSelectMgr::getInstance()->selectionSetImage(DEFAULT_OBJECT_TEXTURE);
+ }
+}
+
void LLPanelFace::onCommitGlow()
{
sendGlow();
@@ -4891,6 +4944,26 @@ bool LLPanelFace::isIdenticalPlanarTexgen()
return (identical_texgen && (selected_texgen == LLTextureEntry::TEX_GEN_PLANAR));
}
+bool LLPanelFace::isMediaTexSelected()
+{
+ LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstNode();
+ if (LLViewerObject* objectp = node->getObject())
+ {
+ S32 num_tes = llmin((S32)objectp->getNumTEs(), (S32)objectp->getNumFaces());
+ for (S32 te = 0; te < num_tes; ++te)
+ {
+ if (node->isTESelected(te))
+ {
+ if (objectp->getTE(te) && objectp->getTE(te)->hasMedia())
+ {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
void LLPanelFace::LLSelectedTE::getFace(LLFace*& face_to_return, bool& identical_face)
{
struct LLSelectedTEGetFace : public LLSelectedTEGetFunctor<LLFace *>
diff --git a/indra/newview/llpanelface.h b/indra/newview/llpanelface.h
index 4d2ce208b4..1ee9bf2cf7 100644
--- a/indra/newview/llpanelface.h
+++ b/indra/newview/llpanelface.h
@@ -139,6 +139,8 @@ protected:
void updateMediaSettings();
void updateMediaTitle();
+ bool isMediaTexSelected();
+
void getState();
void sendTexture(); // applies and sends texture
@@ -238,6 +240,7 @@ protected:
void onCommitShiny();
void onCommitAlphaMode();
void onCommitFullbright();
+ void onCommitHideWater();
void onCommitGlow();
void onCommitPlanarAlign();
void onCommitRepeatsPerMeter();
@@ -308,6 +311,7 @@ private:
LLRadioGroup* mRadioPbrType { nullptr };
LLCheckBoxCtrl* mCheckFullbright { nullptr };
+ LLCheckBoxCtrl* mCheckHideWater{ nullptr };
LLTextBox* mLabelColorTransp { nullptr };
LLSpinCtrl* mCtrlColorTransp { nullptr }; // transparency = 1 - alpha
@@ -555,6 +559,7 @@ private:
LLMenuButton* mMenuClipboardTexture;
bool mIsAlpha;
+ bool mExcludeWater { false };
LLSD mClipboardParams;
diff --git a/indra/newview/llpanelprimmediacontrols.cpp b/indra/newview/llpanelprimmediacontrols.cpp
index b8c12ce0b9..b00b9d1ad1 100644
--- a/indra/newview/llpanelprimmediacontrols.cpp
+++ b/indra/newview/llpanelprimmediacontrols.cpp
@@ -294,7 +294,7 @@ void LLPanelPrimMediaControls::updateShape()
LLViewerMediaImpl* media_impl = getTargetMediaImpl();
LLViewerObject* objectp = getTargetObject();
- if(!media_impl || gFloaterTools->getVisible())
+ if(!media_impl || (gFloaterTools && gFloaterTools->getVisible()))
{
setVisible(false);
return;
diff --git a/indra/newview/llpanelprofilepicks.cpp b/indra/newview/llpanelprofilepicks.cpp
index 08f3d3af5a..09b8011ce4 100644
--- a/indra/newview/llpanelprofilepicks.cpp
+++ b/indra/newview/llpanelprofilepicks.cpp
@@ -55,6 +55,8 @@
static LLPanelInjector<LLPanelProfilePicks> t_panel_profile_picks("panel_profile_picks");
static LLPanelInjector<LLPanelProfilePick> t_panel_profile_pick("panel_profile_pick");
+constexpr F32 REQUEST_TIMOUT = 60;
+constexpr F32 LOCATION_CACHE_TIMOUT = 900;
class LLPickHandler : public LLCommandHandler
{
@@ -306,6 +308,7 @@ void LLPanelProfilePicks::processProperties(void* data, EAvatarProcessorType typ
void LLPanelProfilePicks::processProperties(const LLAvatarData* avatar_picks)
{
+ LL_DEBUGS("PickInfo") << "Processing picks for avatar " << getAvatarId() << LL_ENDL;
LLUUID selected_id = mPickToSelectOnLoad;
bool has_selection = false;
if (mPickToSelectOnLoad.isNull())
@@ -320,6 +323,25 @@ void LLPanelProfilePicks::processProperties(const LLAvatarData* avatar_picks)
}
}
+ // Avoid pointlesly requesting parcel data,
+ // store previous values
+ std::map<LLUUID, std::string> parcelid_location_map;
+ std::map<LLUUID, LLUUID> pickid_parcelid_map;
+
+ for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx)
+ {
+ LLPanelProfilePick* pick_panel = dynamic_cast<LLPanelProfilePick*>(mTabContainer->getPanelByIndex(tab_idx));
+ if (pick_panel && pick_panel->getPickId().notNull())
+ {
+ std::string location = pick_panel->getPickLocation();
+ if (!location.empty())
+ {
+ parcelid_location_map[pick_panel->getParcelID()] = pick_panel->getPickLocation();
+ pickid_parcelid_map[pick_panel->getPickId()] = pick_panel->getParcelID();
+ }
+ }
+ }
+
mTabContainer->deleteAllTabs();
LLAvatarData::picks_list_t::const_iterator it = avatar_picks->picks_list.begin();
@@ -334,6 +356,15 @@ void LLPanelProfilePicks::processProperties(const LLAvatarData* avatar_picks)
pick_panel->setPickName(pick_name);
pick_panel->setAvatarId(getAvatarId());
+ std::map<LLUUID, LLUUID>::const_iterator found_pick = pickid_parcelid_map.find(pick_id);
+ if (found_pick != pickid_parcelid_map.end())
+ {
+ std::map<LLUUID, std::string>::const_iterator found = parcelid_location_map.find(found_pick->second);
+ if (found != parcelid_location_map.end() && !found->second.empty())
+ {
+ pick_panel->setPickLocation(found_pick->second, found->second);
+ }
+ }
mTabContainer->addTabPanel(
LLTabContainer::TabPanelParams().
panel(pick_panel).
@@ -353,6 +384,11 @@ void LLPanelProfilePicks::processProperties(const LLAvatarData* avatar_picks)
LLPanelProfilePick* pick_panel = LLPanelProfilePick::create();
pick_panel->setAvatarId(getAvatarId());
+ std::map<LLUUID, std::string>::const_iterator found = parcelid_location_map.find(data.parcel_id);
+ if (found != parcelid_location_map.end() && !found->second.empty())
+ {
+ pick_panel->setPickLocation(data.parcel_id, found->second);
+ }
pick_panel->processProperties(&data);
mTabContainer->addTabPanel(
LLTabContainer::TabPanelParams().
@@ -638,9 +674,14 @@ void LLPanelProfilePick::processProperties(void* data, EAvatarProcessorType type
void LLPanelProfilePick::processProperties(const LLPickData* pick_info)
{
+ LL_DEBUGS("PickInfo") << "Processing properties for pick " << mPickId << LL_ENDL;
mIsEditing = false;
mPickDescription->setParseHTML(true);
- mParcelId = pick_info->parcel_id;
+ if (mParcelId != pick_info->parcel_id)
+ {
+ mParcelId = pick_info->parcel_id;
+ mPickLocationStr.clear();
+ }
setSnapshotId(pick_info->snapshot_id);
if (!getSelfProfile())
{
@@ -650,8 +691,11 @@ void LLPanelProfilePick::processProperties(const LLPickData* pick_info)
setPickDesc(pick_info->desc);
setPosGlobal(pick_info->pos_global);
- // Send remote parcel info request to get parcel name and sim (region) name.
- sendParcelInfoRequest();
+ if (mPickLocationStr.empty() || mLastRequestTimer.getElapsedTimeF32() > LOCATION_CACHE_TIMOUT)
+ {
+ // Send remote parcel info request to get parcel name and sim (region) name.
+ sendParcelInfoRequest();
+ }
// *NOTE dzaporozhan
// We want to keep listening to APT_PICK_INFO because user may
@@ -691,9 +735,17 @@ void LLPanelProfilePick::setPickDesc(const std::string& desc)
mPickDescription->setValue(desc);
}
+void LLPanelProfilePick::setPickLocation(const LLUUID &parcel_id, const std::string& location)
+{
+ setParcelID(parcel_id); // resets mPickLocationStr
+ setPickLocation(location);
+}
+
void LLPanelProfilePick::setPickLocation(const std::string& location)
{
getChild<LLUICtrl>("pick_location")->setValue(location);
+ mPickLocationStr = location;
+ mLastRequestTimer.reset();
}
void LLPanelProfilePick::onClickMap()
@@ -790,16 +842,19 @@ std::string LLPanelProfilePick::getLocationNotice()
void LLPanelProfilePick::sendParcelInfoRequest()
{
- if (mParcelId != mRequestedId)
+ if (mParcelId != mRequestedId || mLastRequestTimer.getElapsedTimeF32() > REQUEST_TIMOUT)
{
if (mRequestedId.notNull())
{
LLRemoteParcelInfoProcessor::getInstance()->removeObserver(mRequestedId, this);
}
+ LL_DEBUGS("PickInfo") << "Sending parcel request " << mParcelId << " for pick " << mPickId << LL_ENDL;
LLRemoteParcelInfoProcessor::getInstance()->addObserver(mParcelId, this);
LLRemoteParcelInfoProcessor::getInstance()->sendParcelInfoRequest(mParcelId);
mRequestedId = mParcelId;
+ mLastRequestTimer.reset();
+ mPickLocationStr.clear();
}
}
@@ -816,6 +871,20 @@ void LLPanelProfilePick::processParcelInfo(const LLParcelData& parcel_data)
}
}
+void LLPanelProfilePick::setParcelID(const LLUUID& parcel_id)
+{
+ if (mParcelId != parcel_id)
+ {
+ mParcelId = parcel_id;
+ mPickLocationStr.clear();
+ }
+ if (mRequestedId.notNull() && mRequestedId != parcel_id)
+ {
+ LLRemoteParcelInfoProcessor::getInstance()->removeObserver(mRequestedId, this);
+ mRequestedId.setNull();
+ }
+}
+
void LLPanelProfilePick::sendUpdate()
{
LLPickData pick_data;
diff --git a/indra/newview/llpanelprofilepicks.h b/indra/newview/llpanelprofilepicks.h
index e3f50f5576..b4d3eb010e 100644
--- a/indra/newview/llpanelprofilepicks.h
+++ b/indra/newview/llpanelprofilepicks.h
@@ -117,6 +117,8 @@ public:
virtual void setPickName(const std::string& name);
const std::string getPickName();
+ virtual void setPickLocation(const LLUUID& parcel_id, const std::string& location);
+ std::string getPickLocation() { return mPickLocationStr; };
void processProperties(void* data, EAvatarProcessorType type) override;
void processProperties(const LLPickData* pick_data);
@@ -135,7 +137,8 @@ public:
//This stuff we got from LLRemoteParcelObserver, in the last one we intentionally do nothing
void processParcelInfo(const LLParcelData& parcel_data) override;
- void setParcelID(const LLUUID& parcel_id) override { mParcelId = parcel_id; }
+ void setParcelID(const LLUUID& parcel_id) override;
+ LLUUID getParcelID() const { return mParcelId; }
void setErrorStatus(S32 status, const std::string& reason) override {};
protected:
@@ -230,6 +233,8 @@ protected:
LLUUID mPickId;
LLUUID mRequestedId;
std::string mPickNameStr;
+ std::string mPickLocationStr;
+ LLTimer mLastRequestTimer;
boost::signals2::connection mRegionCallbackConnection;
boost::signals2::connection mParcelCallbackConnection;
diff --git a/indra/newview/llscrollingpanelparam.h b/indra/newview/llscrollingpanelparam.h
index 3aba4e4e40..93daf22e76 100644
--- a/indra/newview/llscrollingpanelparam.h
+++ b/indra/newview/llscrollingpanelparam.h
@@ -81,7 +81,6 @@ public:
protected:
LLTimer mMouseDownTimer; // timer for how long mouse has been held down on a hint.
F32 mLastHeldTime;
- bool mAllowModify;
LLButton* mLessBtn;
LLButton* mMoreBtn;
diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp
index 03cf79c28c..01eb76c1a3 100644
--- a/indra/newview/llselectmgr.cpp
+++ b/indra/newview/llselectmgr.cpp
@@ -7257,7 +7257,10 @@ void dialog_refresh_all()
// *TODO: Eliminate all calls into outside classes below, make those
// objects register with the update signal.
- gFloaterTools->dirty();
+ if (gFloaterTools)
+ {
+ gFloaterTools->dirty();
+ }
gMenuObject->needsArrange();
@@ -7486,7 +7489,8 @@ void LLSelectMgr::updatePointAt()
LLVector3 select_offset;
const LLPickInfo& pick = gViewerWindow->getLastPick();
LLViewerObject *click_object = pick.getObject();
- if (click_object && click_object->isSelected())
+ bool was_hud = pick.mPickHUD && !click_object->isHUDAttachment();
+ if (click_object && click_object->isSelected() && !was_hud)
{
// clicked on another object in our selection group, use that as target
select_offset.setVec(pick.mObjectOffset);
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 6bf203c140..de673b591f 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -304,16 +304,62 @@ void callback_cache_name(const LLUUID& id, const std::string& full_name, bool is
// exported functionality
//
+void do_startup_frame()
+{
+ // Until after STATE_AGENT_SEND we don't get very many UDP packets to poll the socket,
+ // and after STATE_PRECACHE the LLAppViewer::idleNetwork() will do UDP processing,
+ // so we only bother to process between those two states.
+ EStartupState state = LLStartUp::getStartupState();
+ if (state > STATE_AGENT_SEND && state < STATE_PRECACHE)
+ {
+ // drain the UDP socket...
+ U64 t0 = totalTime();
+ constexpr U64 MAX_STARTUP_FRAME_TIME = 2000; // usec
+ constexpr U64 MAX_STARTUP_FRAME_MESSAGES = 100;
+ S32 num_messages = 0;
+ bool needs_drain = false;
+ LockMessageChecker lmc(gMessageSystem);
+ while (lmc.checkAllMessages(gFrameCount, gServicePump))
+ {
+ if (gDoDisconnect)
+ {
+ // We're disconnecting, don't process any more messages from the server
+ // We're usually disconnecting due to either network corruption or a
+ // server going down, so this is OK.
+ break;
+ }
+ if (++num_messages >= MAX_STARTUP_FRAME_MESSAGES
+ || (totalTime() - t0) > MAX_STARTUP_FRAME_TIME)
+ {
+ needs_drain = true;
+ break;
+ }
+ }
+ if (needs_drain || gMessageSystem->mPacketRing.getNumBufferedPackets() > 0)
+ {
+ gMessageSystem->drainUdpSocket();
+ }
+ lmc.processAcks();
+ }
+ // ...then call display_startup()
+ display_startup();
+}
+
void pump_idle_startup_network(void)
{
+ // while there are message to process:
+ // process one then call display_startup()
+ S32 num_messages = 0;
{
LockMessageChecker lmc(gMessageSystem);
while (lmc.checkAllMessages(gFrameCount, gServicePump))
{
display_startup();
+ ++num_messages;
}
lmc.processAcks();
}
+ // finally call one last display_startup()
display_startup();
}
@@ -630,21 +676,6 @@ bool idle_startup()
F32 dropPercent = gSavedSettings.getF32("PacketDropPercentage");
msg->mPacketRing.setDropPercentage(dropPercent);
-
- F32 inBandwidth = gSavedSettings.getF32("InBandwidth");
- F32 outBandwidth = gSavedSettings.getF32("OutBandwidth");
- if (inBandwidth != 0.f)
- {
- LL_DEBUGS("AppInit") << "Setting packetring incoming bandwidth to " << inBandwidth << LL_ENDL;
- msg->mPacketRing.setUseInThrottle(true);
- msg->mPacketRing.setInBandwidth(inBandwidth);
- }
- if (outBandwidth != 0.f)
- {
- LL_DEBUGS("AppInit") << "Setting packetring outgoing bandwidth to " << outBandwidth << LL_ENDL;
- msg->mPacketRing.setUseOutThrottle(true);
- msg->mPacketRing.setOutBandwidth(outBandwidth);
- }
}
LL_INFOS("AppInit") << "Message System Initialized." << LL_ENDL;
@@ -770,7 +801,7 @@ bool idle_startup()
LL_DEBUGS("AppInit") << "STATE_BROWSER_INIT" << LL_ENDL;
std::string msg = LLTrans::getString("LoginInitializingBrowser");
set_startup_status(0.03f, msg.c_str(), gAgent.mMOTD.c_str());
- display_startup();
+ do_startup_frame();
// LLViewerMedia::initBrowser();
LLStartUp::setStartupState( STATE_LOGIN_SHOW );
return false;
@@ -835,7 +866,7 @@ bool idle_startup()
LL_DEBUGS("AppInit") << "FirstLoginThisInstall off" << LL_ENDL;
}
}
- display_startup();
+ do_startup_frame();
LLStartUp::setStartupState( STATE_LOGIN_WAIT ); // Wait for user input
}
else
@@ -866,7 +897,7 @@ bool idle_startup()
}
LL_DEBUGS("AppInit") << "PeekMessage processed" << LL_ENDL;
#endif
- display_startup();
+ do_startup_frame();
timeout.reset();
return false;
}
@@ -881,7 +912,7 @@ bool idle_startup()
// Don't do anything. Wait for the login view to call the login_callback,
// which will push us to the next state.
- // display() function will be the one to run display_startup()
+ // display() function will be the one to run do_startup_frame()
// Sleep so we don't spin the CPU
ms_sleep(1);
return false;
@@ -1063,7 +1094,7 @@ bool idle_startup()
auth_desc = LLTrans::getString("LoginInProgress");
set_startup_status(progress, auth_desc, auth_message);
progress += 0.02f;
- display_startup();
+ do_startup_frame();
// Setting initial values...
LLLoginInstance* login = LLLoginInstance::getInstance();
@@ -1100,7 +1131,7 @@ bool idle_startup()
emsg << LLTrans::getString("LoginFailedHeader") << "\n";
if(LLLoginInstance::getInstance()->authFailure())
{
- LL_INFOS("LLStartup") << "Login failed, LLLoginInstance::getResponse(): "
+ LL_INFOS("LLStartUp") << "Login failed, LLLoginInstance::getResponse(): "
<< LLLoginInstance::getInstance()->getResponse() << LL_ENDL;
LLSD response = LLLoginInstance::getInstance()->getResponse();
// Still have error conditions that may need some
@@ -1172,7 +1203,7 @@ bool idle_startup()
// If optional was skipped this case shouldn't
// be reached.
- LL_INFOS("LLStartup") << "Forcing a quit due to update." << LL_ENDL;
+ LL_INFOS("LLStartUp") << "Forcing a quit due to update." << LL_ENDL;
LLLoginInstance::getInstance()->disconnect();
LLAppViewer::instance()->forceQuit();
}
@@ -1200,7 +1231,7 @@ bool idle_startup()
}
catch (LLCertException &cert_exception)
{
- LL_WARNS("LLStartup", "SECAPI") << "Caught " << cert_exception.what() << " certificate expception on getCertificate("<< response["certificate"] << ")" << LL_ENDL;
+ LL_WARNS("LLStartUp", "SECAPI") << "Caught " << cert_exception.what() << " certificate expception on getCertificate("<< response["certificate"] << ")" << LL_ENDL;
LLSD args;
args["REASON"] = LLTrans::getString(cert_exception.what());
@@ -1252,7 +1283,7 @@ bool idle_startup()
// notificatioin message.
LLSD args;
args["ERROR_MESSAGE"] = emsg.str();
- LL_INFOS("LLStartup") << "Notification: " << args << LL_ENDL;
+ LL_INFOS("LLStartUp") << "Notification: " << args << LL_ENDL;
LLNotificationsUtil::add("ErrorMessage", args, LLSD(), login_alert_done);
}
}
@@ -1275,7 +1306,7 @@ bool idle_startup()
{
LLSD args;
args["ERROR_MESSAGE"] = emsg.str();
- LL_INFOS("LLStartup") << "Notification: " << args << LL_ENDL;
+ LL_INFOS("LLStartUp") << "Notification: " << args << LL_ENDL;
LLNotificationsUtil::add("ErrorMessage", args, LLSD(), login_alert_done);
transition_back_to_login_panel(emsg.str());
show_connect_box = true;
@@ -1291,71 +1322,71 @@ bool idle_startup()
if (STATE_WORLD_INIT == LLStartUp::getStartupState())
{
set_startup_status(0.30f, LLTrans::getString("LoginInitializingWorld"), gAgent.mMOTD);
- display_startup();
+ do_startup_frame();
// We should have an agent id by this point.
llassert(!(gAgentID == LLUUID::null));
// Finish agent initialization. (Requires gSavedSettings, builds camera)
gAgent.init();
- display_startup();
+ do_startup_frame();
gAgentCamera.init();
- display_startup();
- display_startup();
+ do_startup_frame();
+ do_startup_frame();
// Since we connected, save off the settings so the user doesn't have to
// type the name/password again if we crash.
gSavedSettings.saveToFile(gSavedSettings.getString("ClientSettingsFile"), true);
LLUIColorTable::instance().saveUserSettings();
- display_startup();
+ do_startup_frame();
//
// Initialize classes w/graphics stuff.
//
LLSurface::initClasses();
- display_startup();
+ do_startup_frame();
- display_startup();
+ do_startup_frame();
LLDrawable::initClass();
- display_startup();
+ do_startup_frame();
// init the shader managers
LLPostProcess::initClass();
- display_startup();
+ do_startup_frame();
LLAvatarAppearance::initClass("avatar_lad.xml","avatar_skeleton.xml");
- display_startup();
+ do_startup_frame();
LLViewerObject::initVOClasses();
- display_startup();
+ do_startup_frame();
// Initialize all our tools. Must be done after saved settings loaded.
// NOTE: This also is where gToolMgr used to be instantiated before being turned into a singleton.
LLToolMgr::getInstance()->initTools();
- display_startup();
+ do_startup_frame();
// Pre-load floaters, like the world map, that are slow to spawn
// due to XML complexity.
gViewerWindow->initWorldUI();
- display_startup();
+ do_startup_frame();
// This is where we used to initialize gWorldp. Original comment said:
// World initialization must be done after above window init
// User might have overridden far clip
LLWorld::getInstance()->setLandFarClip(gAgentCamera.mDrawDistance);
- display_startup();
+ do_startup_frame();
// Before we create the first region, we need to set the agent's mOriginGlobal
// This is necessary because creating objects before this is set will result in a
// bad mPositionAgent cache.
gAgent.initOriginGlobal(from_region_handle(gFirstSimHandle));
- display_startup();
+ do_startup_frame();
LLWorld::getInstance()->addRegion(gFirstSimHandle, gFirstSim);
- display_startup();
+ do_startup_frame();
LLViewerRegion *regionp = LLWorld::getInstance()->getRegionFromHandle(gFirstSimHandle);
LL_INFOS("AppInit") << "Adding initial simulator " << regionp->getOriginGlobal() << LL_ENDL;
@@ -1364,18 +1395,18 @@ bool idle_startup()
<< gFirstSimSeedCap << LL_ENDL;
regionp->setSeedCapability(gFirstSimSeedCap);
LL_DEBUGS("AppInit") << "Waiting for seed grant ...." << LL_ENDL;
- display_startup();
+ do_startup_frame();
// Set agent's initial region to be the one we just created.
gAgent.setRegion(regionp);
- display_startup();
+ do_startup_frame();
// Set agent's initial position, which will be read by LLVOAvatar when the avatar
// object is created. I think this must be done after setting the region. JC
gAgent.setPositionAgent(agent_start_position_region);
- display_startup();
+ do_startup_frame();
LLStartUp::initExperiences();
- display_startup();
+ do_startup_frame();
// If logging should be enebled, turns it on and loads history from disk
// Note: does not happen on init of singleton because preferences can use
@@ -1396,7 +1427,7 @@ bool idle_startup()
{
LLStartUp::multimediaInit();
LLStartUp::setStartupState( STATE_FONT_INIT );
- display_startup();
+ do_startup_frame();
return false;
}
@@ -1405,7 +1436,7 @@ bool idle_startup()
{
LLStartUp::fontInit();
LLStartUp::setStartupState( STATE_SEED_GRANTED_WAIT );
- display_startup();
+ do_startup_frame();
return false;
}
@@ -1477,7 +1508,7 @@ bool idle_startup()
set_startup_status(0.4f, LLTrans::getString("LoginRequestSeedCapGrant"), gAgent.mMOTD.c_str());
}
}
- display_startup();
+ do_startup_frame();
return false;
}
@@ -1488,7 +1519,7 @@ bool idle_startup()
//---------------------------------------------------------------------
if (STATE_SEED_CAP_GRANTED == LLStartUp::getStartupState())
{
- display_startup();
+ do_startup_frame();
// These textures are not warrantied to be cached, so needs
// to hapen with caps granted
@@ -1497,9 +1528,9 @@ bool idle_startup()
// will init images, should be done with caps, but before gSky.init()
LLEnvironment::getInstance()->initSingleton();
- display_startup();
+ do_startup_frame();
update_texture_fetch();
- display_startup();
+ do_startup_frame();
if ( gViewerWindow != NULL)
{ // This isn't the first logon attempt, so show the UI
@@ -1507,15 +1538,15 @@ bool idle_startup()
}
gLoginMenuBarView->setVisible( false );
gLoginMenuBarView->setEnabled( false );
- display_startup();
+ do_startup_frame();
// direct logging to the debug console's line buffer
LLError::logToFixedBuffer(gDebugView->mDebugConsolep);
- display_startup();
+ do_startup_frame();
// set initial visibility of debug console
gDebugView->mDebugConsolep->setVisible(gSavedSettings.getBOOL("ShowDebugConsole"));
- display_startup();
+ do_startup_frame();
//
// Set message handlers
@@ -1524,23 +1555,23 @@ bool idle_startup()
// register callbacks for messages. . . do this after initial handshake to make sure that we don't catch any unwanted
register_viewer_callbacks(gMessageSystem);
- display_startup();
+ do_startup_frame();
// Debugging info parameters
gMessageSystem->setMaxMessageTime( 0.5f ); // Spam if decoding all msgs takes more than 500 ms
- display_startup();
+ do_startup_frame();
#ifndef LL_RELEASE_FOR_DOWNLOAD
gMessageSystem->setTimeDecodes( true ); // Time the decode of each msg
gMessageSystem->setTimeDecodesSpamThreshold( 0.05f ); // Spam if a single msg takes over 50ms to decode
#endif
- display_startup();
+ do_startup_frame();
gXferManager->registerCallbacks(gMessageSystem);
- display_startup();
+ do_startup_frame();
LLStartUp::initNameCache();
- display_startup();
+ do_startup_frame();
// update the voice settings *after* gCacheName initialization
// so that we can construct voice UI that relies on the name cache
@@ -1548,7 +1579,7 @@ bool idle_startup()
{
LLVoiceClient::getInstance()->updateSettings();
}
- display_startup();
+ do_startup_frame();
// create a container's instance for start a controlling conversation windows
// by the voice's events
@@ -1569,12 +1600,12 @@ bool idle_startup()
// register null callbacks for audio until the audio system is initialized
gMessageSystem->setHandlerFuncFast(_PREHASH_SoundTrigger, null_message_callback, NULL);
gMessageSystem->setHandlerFuncFast(_PREHASH_AttachedSound, null_message_callback, NULL);
- display_startup();
+ do_startup_frame();
//reset statistics
LLViewerStats::instance().resetStats();
- display_startup();
+ do_startup_frame();
//
// Set up region and surface defaults
//
@@ -1599,7 +1630,7 @@ bool idle_startup()
LLViewerCamera::getInstance()->setAspect(gViewerWindow->getWorldViewAspectRatio());
// Initialize FOV
LLViewerCamera::getInstance()->setDefaultFOV(gSavedSettings.getF32("CameraAngle"));
- display_startup();
+ do_startup_frame();
// Move agent to starting location. The position handed to us by
// the space server is in global coordinates, but the agent frame
@@ -1610,7 +1641,7 @@ bool idle_startup()
gAgent.resetAxes(gAgentStartLookAt);
gAgentCamera.stopCameraAnimation();
gAgentCamera.resetCamera();
- display_startup();
+ do_startup_frame();
// Initialize global class data needed for surfaces (i.e. textures)
LL_DEBUGS("AppInit") << "Initializing sky..." << LL_ENDL;
@@ -1621,7 +1652,7 @@ bool idle_startup()
LLGLState::checkStates();
- display_startup();
+ do_startup_frame();
LL_DEBUGS("AppInit") << "Decoding images..." << LL_ENDL;
// For all images pre-loaded into viewer cache, init
@@ -1635,12 +1666,12 @@ bool idle_startup()
{
F32 frac = (F32)i / (F32)DECODE_TIME_SEC;
set_startup_status(0.45f + frac*0.1f, LLTrans::getString("LoginDecodingImages"), gAgent.mMOTD);
- display_startup();
+ do_startup_frame();
gTextureList.decodeAllImages(1.f);
}
LLStartUp::setStartupState( STATE_WORLD_WAIT );
- display_startup();
+ do_startup_frame();
// JC - Do this as late as possible to increase likelihood Purify
// will run.
@@ -1669,7 +1700,7 @@ bool idle_startup()
NULL);
timeout.reset();
- display_startup();
+ do_startup_frame();
return false;
}
@@ -1696,7 +1727,7 @@ bool idle_startup()
{
LL_DEBUGS("AppInit") << "Connecting to region..." << LL_ENDL;
set_startup_status(0.60f, LLTrans::getString("LoginConnectingToRegion"), gAgent.mMOTD);
- display_startup();
+ do_startup_frame();
// register with the message system so it knows we're
// expecting this message
LLMessageSystem* msg = gMessageSystem;
@@ -1710,7 +1741,7 @@ bool idle_startup()
gAssetStorage->setUpstream(regionp->getHost());
gCacheName->setUpstream(regionp->getHost());
}
- display_startup();
+ do_startup_frame();
// Create login effect
// But not on first login, because you can't see your avatar then
@@ -1725,7 +1756,7 @@ bool idle_startup()
LLStartUp::setStartupState( STATE_AGENT_WAIT ); // Go to STATE_AGENT_WAIT
timeout.reset();
- display_startup();
+ do_startup_frame();
return false;
}
@@ -1734,35 +1765,13 @@ bool idle_startup()
//---------------------------------------------------------------------
if (STATE_AGENT_WAIT == LLStartUp::getStartupState())
{
- {
- LockMessageChecker lmc(gMessageSystem);
- while (lmc.checkAllMessages(gFrameCount, gServicePump))
- {
- if (gAgentMovementCompleted)
- {
- // Sometimes we have more than one message in the
- // queue. break out of this loop and continue
- // processing. If we don't, then this could skip one
- // or more login steps.
- break;
- }
- else
- {
- LL_DEBUGS("AppInit") << "Awaiting AvatarInitComplete, got "
- << gMessageSystem->getMessageName() << LL_ENDL;
- }
- display_startup();
- }
- lmc.processAcks();
- }
-
- display_startup();
+ do_startup_frame();
if (gAgentMovementCompleted)
{
LLStartUp::setStartupState( STATE_INVENTORY_SEND );
}
- display_startup();
+ do_startup_frame();
if (!gAgentMovementCompleted && timeout.getElapsedTimeF32() > STATE_AGENT_WAIT_TIMEOUT)
{
@@ -1795,7 +1804,7 @@ bool idle_startup()
if (STATE_INVENTORY_SEND == LLStartUp::getStartupState())
{
LL_PROFILE_ZONE_NAMED("State inventory send")
- display_startup();
+ do_startup_frame();
// request mute list
LL_INFOS() << "Requesting Mute List" << LL_ENDL;
@@ -1805,12 +1814,12 @@ bool idle_startup()
LL_INFOS() << "Requesting Money Balance" << LL_ENDL;
LLStatusBar::sendMoneyBalanceRequest();
- display_startup();
+ do_startup_frame();
// Inform simulator of our language preference
LLAgentLanguage::update();
- display_startup();
+ do_startup_frame();
// unpack thin inventory
LLSD response = LLLoginInstance::getInstance()->getResponse();
//bool dump_buffer = false;
@@ -1825,7 +1834,7 @@ bool idle_startup()
gInventory.setLibraryRootFolderID(id.asUUID());
}
}
- display_startup();
+ do_startup_frame();
LLSD inv_lib_owner = response["inventory-lib-owner"];
if(inv_lib_owner.isDefined())
@@ -1837,9 +1846,9 @@ bool idle_startup()
gInventory.setLibraryOwnerID(LLUUID(id.asUUID()));
}
}
- display_startup();
+ do_startup_frame();
LLStartUp::setStartupState(STATE_INVENTORY_SKEL);
- display_startup();
+ do_startup_frame();
return false;
}
@@ -1858,7 +1867,7 @@ bool idle_startup()
LL_WARNS("AppInit") << "Problem loading inventory-skel-lib" << LL_ENDL;
}
}
- display_startup();
+ do_startup_frame();
LLSD inv_skeleton = response["inventory-skeleton"];
if (inv_skeleton.isDefined())
@@ -1869,9 +1878,9 @@ bool idle_startup()
LL_WARNS("AppInit") << "Problem loading inventory-skel-targets" << LL_ENDL;
}
}
- display_startup();
+ do_startup_frame();
LLStartUp::setStartupState(STATE_INVENTORY_SEND2);
- display_startup();
+ do_startup_frame();
return false;
}
@@ -1917,7 +1926,7 @@ bool idle_startup()
list[agent_id] = new LLRelationship(given_rights, has_rights, false);
}
LLAvatarTracker::instance().addBuddyList(list);
- display_startup();
+ do_startup_frame();
}
bool show_hud = false;
@@ -1945,7 +1954,7 @@ bool idle_startup()
//}
}
}
- display_startup();
+ do_startup_frame();
// Either we want to show tutorial because this is the first login
// to a Linden Help Island or the user quit with the tutorial
@@ -1954,21 +1963,21 @@ bool idle_startup()
{
LLFloaterReg::showInstance("hud", LLSD(), false);
}
- display_startup();
+ do_startup_frame();
LLSD event_notifications = response["event_notifications"];
if(event_notifications.isDefined())
{
gEventNotifier.load(event_notifications);
}
- display_startup();
+ do_startup_frame();
LLSD classified_categories = response["classified_categories"];
if(classified_categories.isDefined())
{
LLClassifiedInfo::loadCategories(classified_categories);
}
- display_startup();
+ do_startup_frame();
// This method MUST be called before gInventory.findCategoryUUIDForType because of
// gInventory.mIsAgentInvUsable is set to true in the gInventory.buildParentChildMap.
@@ -1987,7 +1996,7 @@ bool idle_startup()
LLInventoryModelBackgroundFetch::instance().start();
gInventory.createCommonSystemCategories();
LLStartUp::setStartupState(STATE_INVENTORY_CALLBACKS );
- display_startup();
+ do_startup_frame();
return false;
}
@@ -1999,7 +2008,7 @@ bool idle_startup()
{
if (!LLInventoryModel::isSysFoldersReady())
{
- display_startup();
+ do_startup_frame();
return false;
}
@@ -2024,7 +2033,7 @@ bool idle_startup()
gInventory.addChangedMask(LLInventoryObserver::ALL, LLUUID::null);
gInventory.notifyObservers();
- display_startup();
+ do_startup_frame();
// set up callbacks
LL_INFOS() << "Registering Callbacks" << LL_ENDL;
@@ -2035,18 +2044,18 @@ bool idle_startup()
LLAvatarTracker::instance().registerCallbacks(msg);
LL_INFOS() << " Landmark" << LL_ENDL;
LLLandmark::registerCallbacks(msg);
- display_startup();
+ do_startup_frame();
// request all group information
LL_INFOS() << "Requesting Agent Data" << LL_ENDL;
gAgent.sendAgentDataUpdateRequest();
- display_startup();
+ do_startup_frame();
// Create the inventory views
LL_INFOS() << "Creating Inventory Views" << LL_ENDL;
LLFloaterReg::getInstance("inventory");
- display_startup();
+ do_startup_frame();
LLStartUp::setStartupState( STATE_MISC );
- display_startup();
+ do_startup_frame();
return false;
}
@@ -2096,7 +2105,7 @@ bool idle_startup()
gSavedSettings.setBOOL("ShowStartLocation", true);
}
- display_startup();
+ do_startup_frame();
// Load stored local environment if needed.
LLEnvironment::instance().loadFromSettings();
@@ -2104,7 +2113,7 @@ bool idle_startup()
// *TODO : Uncomment that line once the whole grid migrated to SLM and suppress it from LLAgent::handleTeleportFinished() (llagent.cpp)
//check_merchant_status();
- display_startup();
+ do_startup_frame();
if (gSavedSettings.getBOOL("HelpFloaterOpen"))
{
@@ -2112,7 +2121,7 @@ bool idle_startup()
LLViewerHelp::instance().showTopic("");
}
- display_startup();
+ do_startup_frame();
// We're successfully logged in.
gSavedSettings.setBOOL("FirstLoginThisInstall", false);
@@ -2121,12 +2130,12 @@ bool idle_startup()
LLFloaterGridStatus::getInstance()->startGridStatusTimer();
- display_startup();
+ do_startup_frame();
- display_startup();
+ do_startup_frame();
// JC: Initializing audio requests many sounds for download.
init_audio();
- display_startup();
+ do_startup_frame();
// JC: Initialize "active" gestures. This may also trigger
// many gesture downloads, if this is the user's first
@@ -2164,7 +2173,7 @@ bool idle_startup()
LLGestureMgr::instance().startFetch();
}
gDisplaySwapBuffers = true;
- display_startup();
+ do_startup_frame();
LLMessageSystem* msg = gMessageSystem;
msg->setHandlerFuncFast(_PREHASH_SoundTrigger, process_sound_trigger);
@@ -2242,10 +2251,10 @@ bool idle_startup()
}
}
- display_startup();
+ do_startup_frame();
//DEV-17797. get null folder. Any items found here moved to Lost and Found
LLInventoryModelBackgroundFetch::instance().findLostItems();
- display_startup();
+ do_startup_frame();
LLStartUp::setStartupState( STATE_PRECACHE );
timeout.reset();
@@ -2254,7 +2263,7 @@ bool idle_startup()
if (STATE_PRECACHE == LLStartUp::getStartupState())
{
- display_startup();
+ do_startup_frame();
F32 timeout_frac = timeout.getElapsedTimeF32()/PRECACHING_DELAY;
// We now have an inventory skeleton, so if this is a user's first
@@ -2280,7 +2289,7 @@ bool idle_startup()
callAfterCOFFetch(set_flags_and_update_appearance);
}
- display_startup();
+ do_startup_frame();
// wait precache-delay and for agent's avatar or a lot longer.
if ((timeout_frac > 1.f) && isAgentAvatarValid())
@@ -2305,7 +2314,7 @@ bool idle_startup()
set_startup_status(0.60f + 0.30f * timeout_frac,
LLTrans::getString("LoginPrecaching"),
gAgent.mMOTD.c_str());
- display_startup();
+ do_startup_frame();
}
return true;
@@ -2333,7 +2342,7 @@ bool idle_startup()
LLStartUp::setStartupState( STATE_CLEANUP );
}
- display_startup();
+ do_startup_frame();
if (gAgent.isOutfitChosen() && (wearables_time > MAX_WEARABLES_TIME))
{
@@ -2374,7 +2383,7 @@ bool idle_startup()
if (STATE_CLEANUP == LLStartUp::getStartupState())
{
set_startup_status(1.0, "", "");
- display_startup();
+ do_startup_frame();
if (!mBenefitsSuccessfullyInit)
{
@@ -2395,7 +2404,7 @@ bool idle_startup()
//gViewerWindow->revealIntroPanel();
gViewerWindow->setStartupComplete();
gViewerWindow->setProgressCancelButtonVisible(false);
- display_startup();
+ do_startup_frame();
// We're not away from keyboard, even though login might have taken
// a while. JC
@@ -2427,7 +2436,7 @@ bool idle_startup()
// LLUserAuth::getInstance()->reset();
LLStartUp::setStartupState( STATE_STARTED );
- display_startup();
+ do_startup_frame();
// Unmute audio if desired and setup volumes.
// This is a not-uncommon crash site, so surround it with
@@ -2443,7 +2452,7 @@ bool idle_startup()
LLAgentPicksInfo::getInstance()->requestNumberOfPicks();
- display_startup();
+ do_startup_frame();
llassert(LLPathfindingManager::getInstance() != NULL);
LLPathfindingManager::getInstance()->initSystem();
@@ -3056,9 +3065,7 @@ std::string LLStartUp::startupStateToString(EStartupState state)
// static
void LLStartUp::setStartupState( EStartupState state )
{
- LL_INFOS("AppInit") << "Startup state changing from " <<
- getStartupStateString() << " to " <<
- startupStateToString(state) << LL_ENDL;
+ LL_INFOS("AppInit") << getStartupStateString() << " --> " << startupStateToString(state) << LL_ENDL;
getPhases().stopPhase(getStartupStateString());
gStartupState = state;
@@ -3122,7 +3129,7 @@ void LLStartUp::multimediaInit()
LL_DEBUGS("AppInit") << "Initializing Multimedia...." << LL_ENDL;
std::string msg = LLTrans::getString("LoginInitializingMultimedia");
set_startup_status(0.42f, msg.c_str(), gAgent.mMOTD.c_str());
- display_startup();
+ do_startup_frame();
}
void LLStartUp::fontInit()
@@ -3130,7 +3137,7 @@ void LLStartUp::fontInit()
LL_DEBUGS("AppInit") << "Initializing fonts...." << LL_ENDL;
std::string msg = LLTrans::getString("LoginInitializingFonts");
set_startup_status(0.45f, msg.c_str(), gAgent.mMOTD.c_str());
- display_startup();
+ do_startup_frame();
LLFontGL::loadDefaultFonts();
}
@@ -3827,14 +3834,14 @@ bool process_login_success_response()
{
// We got an answer from the grid -> use that for map for the current session
gSavedSettings.setString("CurrentMapServerURL", map_server_url);
- LL_INFOS("LLStartup") << "map-server-url : we got an answer from the grid : " << map_server_url << LL_ENDL;
+ LL_INFOS("LLStartUp") << "map-server-url : we got an answer from the grid : " << map_server_url << LL_ENDL;
}
else
{
// No answer from the grid -> use the default setting for current session
map_server_url = gSavedSettings.getString("MapServerURL");
gSavedSettings.setString("CurrentMapServerURL", map_server_url);
- LL_INFOS("LLStartup") << "map-server-url : no map-server-url answer, we use the default setting for the map : " << map_server_url << LL_ENDL;
+ LL_INFOS("LLStartUp") << "map-server-url : no map-server-url answer, we use the default setting for the map : " << map_server_url << LL_ENDL;
}
// Default male and female avatars allowing the user to choose their avatar on first login.
diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp
index bda53f66eb..78d930c05c 100644
--- a/indra/newview/lltextureview.cpp
+++ b/indra/newview/lltextureview.cpp
@@ -642,7 +642,7 @@ void LLGLTexMemBar::draw()
text = llformat("Mesh: Reqs(Tot/Htp/Big): %u/%u/%u Rtr/Err: %u/%u Cread/Cwrite: %u/%u Low/At/High: %d/%d/%d",
LLMeshRepository::sMeshRequestCount, LLMeshRepository::sHTTPRequestCount, LLMeshRepository::sHTTPLargeRequestCount,
LLMeshRepository::sHTTPRetryCount, LLMeshRepository::sHTTPErrorCount,
- LLMeshRepository::sCacheReads, LLMeshRepository::sCacheWrites,
+ (U32)LLMeshRepository::sCacheReads, (U32)LLMeshRepository::sCacheWrites,
LLMeshRepoThread::sRequestLowWater, LLMeshRepoThread::sRequestWaterLevel, LLMeshRepoThread::sRequestHighWater);
LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*2,
text_color, LLFontGL::LEFT, LLFontGL::TOP);
diff --git a/indra/newview/lltoolbrush.cpp b/indra/newview/lltoolbrush.cpp
index 2fe81df4fb..cf7b123fa7 100644
--- a/indra/newview/lltoolbrush.cpp
+++ b/indra/newview/lltoolbrush.cpp
@@ -447,7 +447,10 @@ void LLToolBrushLand::handleSelect()
{
gEditMenuHandler = this;
- gFloaterTools->setStatusText("modifyland");
+ if (gFloaterTools)
+ {
+ gFloaterTools->setStatusText("modifyland");
+ }
// if (!mBrushSelected)
{
mBrushSelected = true;
diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp
index 4efa289141..75adb722f3 100644
--- a/indra/newview/lltooldraganddrop.cpp
+++ b/indra/newview/lltooldraganddrop.cpp
@@ -1253,6 +1253,7 @@ void LLToolDragAndDrop::dropMaterial(LLViewerObject* hit_obj,
// If user dropped a material onto face it implies
// applying texture now without cancel, save to selection
if (nodep
+ && gFloaterTools
&& gFloaterTools->getVisible()
&& nodep->mSavedGLTFMaterialIds.size() > hit_face)
{
@@ -1429,10 +1430,10 @@ void LLToolDragAndDrop::dropTexture(LLViewerObject* hit_obj,
// If user dropped a texture onto face it implies
// applying texture now without cancel, save to selection
- LLPanelFace* panel_face = gFloaterTools->getPanelFace();
+ LLPanelFace* panel_face = gFloaterTools ? gFloaterTools->getPanelFace() : nullptr;
if (nodep
- && gFloaterTools->getVisible()
&& panel_face
+ && gFloaterTools->getVisible()
&& panel_face->getTextureDropChannel() == 0 /*texture*/
&& nodep->mSavedTextures.size() > hit_face)
{
@@ -1488,8 +1489,8 @@ void LLToolDragAndDrop::dropTextureOneFace(LLViewerObject* hit_obj,
if (allow_adding_to_override)
{
LLGLTFMaterial::TextureInfo drop_channel = LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR;
- LLPanelFace* panel_face = gFloaterTools->getPanelFace();
- if (gFloaterTools->getVisible() && panel_face)
+ LLPanelFace* panel_face = gFloaterTools ? gFloaterTools->getPanelFace() : nullptr;
+ if (panel_face && gFloaterTools->getVisible())
{
drop_channel = panel_face->getPBRDropChannel();
}
@@ -1514,9 +1515,9 @@ void LLToolDragAndDrop::dropTextureOneFace(LLViewerObject* hit_obj,
LLTextureEntry* tep = hit_obj->getTE(hit_face);
- LLPanelFace* panel_face = gFloaterTools->getPanelFace();
+ LLPanelFace* panel_face = gFloaterTools ? gFloaterTools->getPanelFace() : nullptr;
- if (gFloaterTools->getVisible() && panel_face)
+ if (panel_face && gFloaterTools->getVisible())
{
tex_channel = (tex_channel > -1) ? tex_channel : panel_face->getTextureDropChannel();
switch (tex_channel)
@@ -1611,7 +1612,10 @@ void LLToolDragAndDrop::dropScript(LLViewerObject* hit_obj,
}
}
hit_obj->saveScript(new_script, active, true);
- gFloaterTools->dirty();
+ if (gFloaterTools)
+ {
+ gFloaterTools->dirty();
+ }
// VEFFECT: SetScript
LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, true);
@@ -1844,7 +1848,10 @@ void LLToolDragAndDrop::dropInventory(LLViewerObject* hit_obj,
effectp->setTargetObject(hit_obj);
effectp->setDuration(LL_HUD_DUR_SHORT);
effectp->setColor(LLColor4U(gAgent.getEffectColor()));
- gFloaterTools->dirty();
+ if (gFloaterTools)
+ {
+ gFloaterTools->dirty();
+ }
}
// accessor that looks at permissions, copyability, and names of
diff --git a/indra/newview/lltoolplacer.cpp b/indra/newview/lltoolplacer.cpp
index b15bb5efd5..0d141d7545 100644
--- a/indra/newview/lltoolplacer.cpp
+++ b/indra/newview/lltoolplacer.cpp
@@ -527,7 +527,10 @@ bool LLToolPlacer::handleHover(S32 x, S32 y, MASK mask)
void LLToolPlacer::handleSelect()
{
- gFloaterTools->setStatusText("place");
+ if (gFloaterTools)
+ {
+ gFloaterTools->setStatusText("place");
+ }
}
void LLToolPlacer::handleDeselect()
diff --git a/indra/newview/lltoolselectland.cpp b/indra/newview/lltoolselectland.cpp
index 88553c7557..331581fd88 100644
--- a/indra/newview/lltoolselectland.cpp
+++ b/indra/newview/lltoolselectland.cpp
@@ -207,7 +207,10 @@ void LLToolSelectLand::render()
void LLToolSelectLand::handleSelect()
{
- gFloaterTools->setStatusText("selectland");
+ if (gFloaterTools)
+ {
+ gFloaterTools->setStatusText("selectland");
+ }
}
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index bdae400f1d..1f8f960300 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -291,6 +291,8 @@ void force_error_software_exception();
void force_error_os_exception();
void force_error_driver_crash();
void force_error_coroutine_crash();
+void force_error_coroprocedure_crash();
+void force_error_work_queue_crash();
void force_error_thread_crash();
void handle_force_delete();
@@ -2646,6 +2648,24 @@ class LLAdvancedForceErrorCoroutineCrash : public view_listener_t
}
};
+class LLAdvancedForceErrorCoroprocedureCrash : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ force_error_coroprocedure_crash();
+ return true;
+ }
+};
+
+class LLAdvancedForceErrorWorkQueueCrash : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ force_error_work_queue_crash();
+ return true;
+ }
+};
+
class LLAdvancedForceErrorThreadCrash : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
@@ -3169,7 +3189,11 @@ void handle_object_edit()
LLFloaterReg::showInstance("build");
LLToolMgr::getInstance()->setCurrentToolset(gBasicToolset);
- gFloaterTools->setEditTool( LLToolCompTranslate::getInstance() );
+
+ if (gFloaterTools)
+ {
+ gFloaterTools->setEditTool( LLToolCompTranslate::getInstance() );
+ }
LLViewerJoystick::getInstance()->moveObjects(true);
LLViewerJoystick::getInstance()->setNeedsReset(true);
@@ -8771,6 +8795,16 @@ void force_error_coroutine_crash()
LLAppViewer::instance()->forceErrorCoroutineCrash();
}
+void force_error_coroprocedure_crash()
+{
+ LLAppViewer::instance()->forceErrorCoroprocedureCrash();
+}
+
+void force_error_work_queue_crash()
+{
+ LLAppViewer::instance()->forceErrorWorkQueueCrash();
+}
+
void force_error_thread_crash()
{
LLAppViewer::instance()->forceErrorThreadCrash();
@@ -9978,6 +10012,8 @@ void initialize_menus()
view_listener_t::addMenu(new LLAdvancedForceErrorSoftwareExceptionCoro(), "Advanced.ForceErrorSoftwareExceptionCoro");
view_listener_t::addMenu(new LLAdvancedForceErrorDriverCrash(), "Advanced.ForceErrorDriverCrash");
view_listener_t::addMenu(new LLAdvancedForceErrorCoroutineCrash(), "Advanced.ForceErrorCoroutineCrash");
+ view_listener_t::addMenu(new LLAdvancedForceErrorCoroprocedureCrash(), "Advanced.ForceErrorCoroprocedureCrash");
+ view_listener_t::addMenu(new LLAdvancedForceErrorWorkQueueCrash(), "Advanced.ForceErrorWorkQueueCrash");
view_listener_t::addMenu(new LLAdvancedForceErrorThreadCrash(), "Advanced.ForceErrorThreadCrash");
view_listener_t::addMenu(new LLAdvancedForceErrorDisconnectViewer(), "Advanced.ForceErrorDisconnectViewer");
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index 1d4828fd33..5fd820f91d 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -3147,7 +3147,9 @@ void send_agent_update(bool force_send, bool send_reliable)
LL_PROFILE_ZONE_SCOPED;
llassert(!gCubeSnapshot);
- if (gAgent.getTeleportState() != LLAgent::TELEPORT_NONE)
+ LLAgent::ETeleportState tp_state = gAgent.getTeleportState();
+ if (tp_state != LLAgent::TELEPORT_NONE
+ && tp_state != LLAgent::TELEPORT_ARRIVING)
{
// We don't care if they want to send an agent update, they're not allowed
// until the target simulator is ready to receive them
@@ -3322,7 +3324,36 @@ void send_agent_update(bool force_send, bool send_reliable)
msg->addVector3Fast(_PREHASH_CameraAtAxis, camera_at);
msg->addVector3Fast(_PREHASH_CameraLeftAxis, LLViewerCamera::getInstance()->getLeftAxis());
msg->addVector3Fast(_PREHASH_CameraUpAxis, LLViewerCamera::getInstance()->getUpAxis());
- msg->addF32Fast(_PREHASH_Far, gAgentCamera.mDrawDistance);
+
+ static F32 last_draw_disatance_step = 1024;
+ if (tp_state == LLAgent::TELEPORT_ARRIVING || LLStartUp::getStartupState() < STATE_MISC)
+ {
+ // Inform interest list, prioritize closer area.
+ // Reason: currently server doesn't distance sort attachments, by restricting range
+ // we reduce the number of attachments sent to the viewer, thus prioritizing
+ // closer ones.
+ // Todo: revise and remove once server gets distance sorting.
+ last_draw_disatance_step = llmax((F32)(gAgentCamera.mDrawDistance / 2.f), 50.f);
+ msg->addF32Fast(_PREHASH_Far, last_draw_disatance_step);
+ }
+ else if (last_draw_disatance_step < gAgentCamera.mDrawDistance)
+ {
+ static LLFrameTimer last_step_time;
+ if (last_step_time.getElapsedTimeF32() > 1.f)
+ {
+ // gradually increase draw distance
+ // Idealy this should be not per second, but based on how loaded
+ // mesh thread is, but hopefully this is temporary.
+ last_step_time.reset();
+ F32 step = gAgentCamera.mDrawDistance * 0.1f;
+ last_draw_disatance_step = llmin(last_draw_disatance_step + step, gAgentCamera.mDrawDistance);
+ }
+ msg->addF32Fast(_PREHASH_Far, last_draw_disatance_step);
+ }
+ else
+ {
+ msg->addF32Fast(_PREHASH_Far, gAgentCamera.mDrawDistance);
+ }
msg->addU32Fast(_PREHASH_ControlFlags, control_flags);
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index 41b2c4b44b..8aa9bebb74 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -1290,7 +1290,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
#endif
//clear cost and linkset cost
setObjectCostStale();
- if (isSelected())
+ if (isSelected() && gFloaterTools)
{
gFloaterTools->dirty();
}
@@ -1729,7 +1729,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
#endif
setObjectCostStale();
- if (isSelected())
+ if (isSelected() && gFloaterTools)
{
gFloaterTools->dirty();
}
@@ -3885,7 +3885,7 @@ void LLViewerObject::setObjectCost(F32 cost)
mObjectCost = cost;
mCostStale = false;
- if (isSelected())
+ if (isSelected() && gFloaterTools)
{
gFloaterTools->dirty();
}
@@ -3905,7 +3905,7 @@ void LLViewerObject::setLinksetCost(F32 cost)
iter++;
}
- if (needs_refresh)
+ if (needs_refresh && gFloaterTools)
{
gFloaterTools->dirty();
}
@@ -3916,7 +3916,7 @@ void LLViewerObject::setPhysicsCost(F32 cost)
mPhysicsCost = cost;
mCostStale = false;
- if (isSelected())
+ if (isSelected() && gFloaterTools)
{
gFloaterTools->dirty();
}
@@ -3927,7 +3927,7 @@ void LLViewerObject::setLinksetPhysicsCost(F32 cost)
mLinksetPhysicsCost = cost;
mCostStale = false;
- if (isSelected())
+ if (isSelected() && gFloaterTools)
{
gFloaterTools->dirty();
}
diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h
index 9c9d2f62cf..63458e60ea 100644
--- a/indra/newview/llviewerobject.h
+++ b/indra/newview/llviewerobject.h
@@ -759,6 +759,7 @@ public:
// Associated GLTF Asset
std::shared_ptr<LL::GLTF::Asset> mGLTFAsset;
+ bool mIsGLTFAssetMissing = false;
// Pipeline classes
LLPointer<LLDrawable> mDrawable;
diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp
index 609ad38e96..a0723db479 100644
--- a/indra/newview/llviewertexture.cpp
+++ b/indra/newview/llviewertexture.cpp
@@ -489,7 +489,12 @@ void LLViewerTexture::updateClass()
}
LLViewerMediaTexture::updateClass();
-
+ // This is a divisor used to determine how much VRAM from our overall VRAM budget to use.
+ // This is **cumulative** on whatever the detected or manually set VRAM budget is.
+ // If we detect 2048MB of VRAM, this will, by default, only use 1024.
+ // If you set 1024MB of VRAM, this will, by default, use 512.
+ // -Geenz 2025-03-03
+ static LLCachedControl<U32> tex_vram_divisor(gSavedSettings, "RenderTextureVRAMDivisor", 2);
static LLCachedControl<U32> max_vram_budget(gSavedSettings, "RenderMaxVRAMBudget", 0);
F64 texture_bytes_alloc = LLImageGL::getTextureBytesAllocated() / 1024.0 / 512.0;
@@ -500,6 +505,7 @@ void LLViewerTexture::updateClass()
F32 used = (F32)ll_round(texture_bytes_alloc + vertex_bytes_alloc);
F32 budget = max_vram_budget == 0 ? (F32)gGLManager.mVRAM : (F32)max_vram_budget;
+ budget /= tex_vram_divisor;
// Try to leave at least half a GB for everyone else and for bias,
// but keep at least 768MB for ourselves
diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp
index a4a001eceb..6b4878e7fa 100644
--- a/indra/newview/llviewertexturelist.cpp
+++ b/indra/newview/llviewertexturelist.cpp
@@ -30,6 +30,7 @@
#include "llviewertexturelist.h"
+#include "llagent.h"
#include "llgl.h" // fot gathering stats from GL
#include "llimagegl.h"
#include "llimagebmp.h"
@@ -825,10 +826,19 @@ void LLViewerTextureList::updateImages(F32 max_time)
clearFetchingRequests();
gPipeline.clearRebuildGroups();
cleared = true;
+ return;
}
- return;
+ // ARRIVING is a delay to let things decode, cache and process,
+ // so process textures like normal despite gTeleportDisplay
+ if (gAgent.getTeleportState() != LLAgent::TELEPORT_ARRIVING)
+ {
+ return;
+ }
+ }
+ else
+ {
+ cleared = false;
}
- cleared = false;
LLAppViewer::getTextureFetch()->setTextureBandwidth((F32)LLTrace::get_frame_recording().getPeriodMeanPerSec(LLStatViewer::TEXTURE_NETWORK_DATA_RECEIVED).value());
@@ -901,7 +911,7 @@ void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imag
if (imagep->getBoostLevel() < LLViewerFetchedTexture::BOOST_HIGH) // don't bother checking face list for boosted textures
{
- static LLCachedControl<F32> texture_scale_min(gSavedSettings, "TextureScaleMinAreaFactor", 0.04f);
+ static LLCachedControl<F32> texture_scale_min(gSavedSettings, "TextureScaleMinAreaFactor", 0.0095f);
static LLCachedControl<F32> texture_scale_max(gSavedSettings, "TextureScaleMaxAreaFactor", 25.f);
F32 max_vsize = 0.f;
@@ -943,8 +953,9 @@ void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imag
// Scale desired texture resolution higher or lower depending on texture scale
//
- // Minimum usage examples: a 1024x1024 texture with aplhabet, runing string
- // shows one letter at a time
+ // Minimum usage examples: a 1024x1024 texture with aplhabet (texture atlas),
+ // runing string shows one letter at a time. If texture has ten 100px symbols
+ // per side, minimal scale is (100/1024)^2 = 0.0095
//
// Maximum usage examples: huge chunk of terrain repeats texture
// TODO: make this work with the GLTF texture transforms
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 82a67d50dc..2a71cc05c1 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -6180,7 +6180,7 @@ void LLPickInfo::fetchResults()
mObjectOffset = gAgentCamera.calcFocusOffset(objectp, v_intersection, mPickPt.mX, mPickPt.mY);
mObjectID = objectp->mID;
mObjectFace = (te_offset == NO_FACE) ? -1 : (S32)te_offset;
-
+ mPickHUD = objectp->isHUDAttachment();
mPosGlobal = gAgent.getPosGlobalFromAgent(v_intersection);
diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h
index fbc2c58fbf..ac0dfa3fe4 100644
--- a/indra/newview/llviewerwindow.h
+++ b/indra/newview/llviewerwindow.h
@@ -133,6 +133,7 @@ public:
bool mPickParticle;
bool mPickUnselectable;
bool mPickReflectionProbe = false;
+ bool mPickHUD{ false };
void getSurfaceInfo();
private:
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index f5dfcca873..1cca2161fe 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -683,7 +683,7 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
mFullyLoaded(false),
mPreviousFullyLoaded(false),
mFullyLoadedInitialized(false),
- mLastCloudAttachmentCount(0),
+ mLastCloudAttachmentCount(-1),
mVisualComplexity(VISUAL_COMPLEXITY_UNKNOWN),
mLoadedCallbacksPaused(false),
mLoadedCallbackTextures(0),
@@ -2837,7 +2837,10 @@ void LLVOAvatar::idleUpdate(LLAgent &agent, const F64 &time)
}
// attach objects that were waiting for a drawable
- lazyAttach();
+ if (!mPendingAttachment.empty())
+ {
+ lazyAttach();
+ }
// animate the character
// store off last frame's root position to be consistent with camera position
@@ -3269,6 +3272,7 @@ void LLVOAvatar::idleUpdateLoadingEffect()
if (mFirstFullyVisible)
{
mFirstFullyVisible = false;
+ mLastCloudAttachmentCount = (S32)mSimAttachments.size();
mFirstDecloudTime = mFirstAppearanceMessageTimer.getElapsedTimeF32();
if (isSelf())
{
@@ -7753,6 +7757,64 @@ void LLVOAvatar::cleanupAttachedMesh( LLViewerObject* pVO )
}
}
+bool check_object_for_mesh_loading(LLViewerObject* objectp)
+{
+ if (!objectp || !objectp->getVolume())
+ {
+ return false;
+ }
+ LLVolume* volp = objectp->getVolume();
+ const LLUUID& mesh_id = volp->getParams().getSculptID();
+ if (mesh_id.isNull())
+ {
+ // No mesh nor skin info needed
+ return false;
+ }
+
+ if (volp->isMeshAssetUnavaliable())
+ {
+ // Mesh failed to load, do not expect it
+ return false;
+ }
+
+ if (!objectp->mDrawable)
+ {
+ return false;
+ }
+
+ LLVOVolume* pvobj = objectp->mDrawable->getVOVolume();
+ if (pvobj)
+ {
+ if (!pvobj->isMesh())
+ {
+ // Not a mesh
+ return false;
+ }
+
+ if (!volp->isMeshAssetLoaded())
+ {
+ // Waiting for mesh
+ return true;
+ }
+
+ const LLMeshSkinInfo* skin_data = pvobj->getSkinInfo();
+ if (skin_data)
+ {
+ // Skin info present, done
+ return false;
+ }
+
+ if (pvobj->isSkinInfoUnavaliable())
+ {
+ // Load failed or info not present, don't expect it
+ return false;
+ }
+ }
+
+ // object is not ready
+ return true;
+}
+
bool LLVOAvatar::hasPendingAttachedMeshes()
{
for (attachment_map_t::iterator iter = mAttachmentPoints.begin();
@@ -7767,62 +7829,20 @@ bool LLVOAvatar::hasPendingAttachedMeshes()
++attachment_iter)
{
LLViewerObject* objectp = attachment_iter->get();
- if (objectp)
+ if (objectp && !objectp->isDead())
{
+ if (check_object_for_mesh_loading(objectp))
+ {
+ return true;
+ }
LLViewerObject::const_child_list_t& child_list = objectp->getChildren();
for (LLViewerObject::child_list_t::const_iterator iter1 = child_list.begin();
iter1 != child_list.end(); ++iter1)
{
LLViewerObject* objectchild = *iter1;
- if (objectchild && objectchild->getVolume())
+ if (check_object_for_mesh_loading(objectchild))
{
- const LLUUID& mesh_id = objectchild->getVolume()->getParams().getSculptID();
- if (mesh_id.isNull())
- {
- // No mesh nor skin info needed
- continue;
- }
-
- if (objectchild->getVolume()->isMeshAssetUnavaliable())
- {
- // Mesh failed to load, do not expect it
- continue;
- }
-
- if (objectchild->mDrawable)
- {
- LLVOVolume* pvobj = objectchild->mDrawable->getVOVolume();
- if (pvobj)
- {
- if (!pvobj->isMesh())
- {
- // Not a mesh
- continue;
- }
-
- if (!objectchild->getVolume()->isMeshAssetLoaded())
- {
- // Waiting for mesh
- return true;
- }
-
- const LLMeshSkinInfo* skin_data = pvobj->getSkinInfo();
- if (skin_data)
- {
- // Skin info present, done
- continue;
- }
-
- if (pvobj->isSkinInfoUnavaliable())
- {
- // Load failed or info not present, don't expect it
- continue;
- }
- }
-
- // objectchild is not ready
- return true;
- }
+ return true;
}
}
}
@@ -8425,7 +8445,7 @@ bool LLVOAvatar::updateIsFullyLoaded()
);
// compare amount of attachments to one reported by simulator
- if (!loading && !isSelf() && rez_status < 4 && mLastCloudAttachmentCount < mSimAttachments.size())
+ if (!isSelf() && mLastCloudAttachmentCount < mSimAttachments.size() && mSimAttachments.size() > 0)
{
S32 attachment_count = getAttachmentCount();
if (mLastCloudAttachmentCount != attachment_count)
@@ -8443,6 +8463,11 @@ bool LLVOAvatar::updateIsFullyLoaded()
// waiting
loading = true;
}
+ else if (!loading)
+ {
+ // for hasFirstFullAttachmentData
+ mLastCloudAttachmentCount = (S32)mSimAttachments.size();
+ }
}
}
updateRezzedStatusTimers(rez_status);
@@ -8556,6 +8581,12 @@ bool LLVOAvatar::isFullyLoaded() const
return (mRenderUnloadedAvatar || mFullyLoaded);
}
+bool LLVOAvatar::hasFirstFullAttachmentData() const
+{
+ return !mFirstFullyVisible // Avatar is fully visible, have all data
+ || mLastCloudAttachmentCount >= (S32)mSimAttachments.size();
+}
+
bool LLVOAvatar::isTooComplex() const
{
bool too_complex;
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index dd1725c322..263c3dadf6 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -385,6 +385,7 @@ public:
//--------------------------------------------------------------------
public:
bool isFullyLoaded() const;
+ bool hasFirstFullAttachmentData() const;
F32 getFirstDecloudTime() const {return mFirstDecloudTime;}
// check and return current state relative to limits
diff --git a/indra/newview/llvosky.cpp b/indra/newview/llvosky.cpp
index ab8d0d2564..38c702c4ca 100644
--- a/indra/newview/llvosky.cpp
+++ b/indra/newview/llvosky.cpp
@@ -100,8 +100,16 @@ LLSkyTex::LLSkyTex() :
void LLSkyTex::init(bool isShiny)
{
mIsShiny = isShiny;
- mSkyData = new LLColor4[(U32)(SKYTEX_RESOLUTION * SKYTEX_RESOLUTION)];
- mSkyDirs = new LLVector3[(U32)(SKYTEX_RESOLUTION * SKYTEX_RESOLUTION)];
+ try
+ {
+ mSkyData = new LLColor4[(U32)(SKYTEX_RESOLUTION * SKYTEX_RESOLUTION)];
+ mSkyDirs = new LLVector3[(U32)(SKYTEX_RESOLUTION * SKYTEX_RESOLUTION)];
+ }
+ catch (std::bad_alloc&)
+ {
+ LLError::LLUserWarningMsg::showOutOfMemory();
+ LL_ERRS() << "Failed to allocate memory for sky texture data" << LL_ENDL;
+ }
for (S32 i = 0; i < 2; ++i)
{
diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp
index e0d8c25731..899733ccc3 100644
--- a/indra/newview/llworld.cpp
+++ b/indra/newview/llworld.cpp
@@ -838,6 +838,7 @@ void LLWorld::printPacketsLost()
<< " packets lost: " << cdp->getPacketsLost() << LL_ENDL;
}
}
+ LL_INFOS() << "Packets dropped by Packet Ring: " << gMessageSystem->mPacketRing.getNumDroppedPackets() << LL_ENDL;
}
void LLWorld::processCoarseUpdate(LLMessageSystem* msg, void** user_data)
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index e441e189ad..b40c9fa6db 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -1412,7 +1412,18 @@ void LLPipeline::createLUTBuffers()
{
U32 lightResX = gSavedSettings.getU32("RenderSpecularResX");
U32 lightResY = gSavedSettings.getU32("RenderSpecularResY");
- F32* ls = new F32[lightResX*lightResY];
+ F32* ls = nullptr;
+ try
+ {
+ ls = new F32[lightResX*lightResY];
+ }
+ catch (std::bad_alloc&)
+ {
+ LLError::LLUserWarningMsg::showOutOfMemory();
+ // might be better to set the error into mFatalMessage and rethrow
+ LL_ERRS() << "Bad memory allocation in createLUTBuffers! lightResX: "
+ << lightResX << " lightResY: " << lightResY << LL_ENDL;
+ }
F32 specExp = gSavedSettings.getF32("RenderSpecularExponent");
// Calculate the (normalized) blinn-phong specular lookup texture. (with a few tweaks)
for (U32 y = 0; y < lightResY; ++y)
@@ -7239,7 +7250,7 @@ void LLPipeline::tonemap(LLRenderTarget* src, LLRenderTarget* dst)
LLSettingsSky::ptr_t psky = LLEnvironment::instance().getCurrentSky();
- bool no_post = gSnapshotNoPost || psky->getReflectionProbeAmbiance(should_auto_adjust) == 0.f || (buildNoPost && gFloaterTools->isAvailable());
+ bool no_post = gSnapshotNoPost || psky->getReflectionProbeAmbiance(should_auto_adjust) == 0.f || (buildNoPost && gFloaterTools && gFloaterTools->isAvailable());
LLGLSLShader& shader = no_post ? gNoPostTonemapProgram : gDeferredPostTonemapProgram;
shader.bind();
diff --git a/indra/newview/skins/default/xui/da/floater_avatar_textures.xml b/indra/newview/skins/default/xui/da/floater_avatar_textures.xml
index d0d766eaab..e7fe66c73f 100644
--- a/indra/newview/skins/default/xui/da/floater_avatar_textures.xml
+++ b/indra/newview/skins/default/xui/da/floater_avatar_textures.xml
@@ -14,7 +14,7 @@ teksturer
teksturer
</text>
<button label="Vis IDs på skærm" label_selected="Dump" name="Dump"/>
- <panel name="scroll_content_panel">
+ <panel name="scroll_content_panel2">
<texture_picker label="Hår" name="hair-baked"/>
<texture_picker label="Hår" name="hair_grain"/>
<texture_picker label="Alpha - hår" name="hair_alpha"/>
diff --git a/indra/newview/skins/default/xui/de/floater_avatar_textures.xml b/indra/newview/skins/default/xui/de/floater_avatar_textures.xml
index aed9aa6e78..915ef3067f 100644
--- a/indra/newview/skins/default/xui/de/floater_avatar_textures.xml
+++ b/indra/newview/skins/default/xui/de/floater_avatar_textures.xml
@@ -8,7 +8,7 @@ Texturen</text>
<text name="composite_label">Zusammengesetzte
Texturen</text>
<button label="IDs an Konsole ausgeben" label_selected="Abladen" name="Dump"/>
- <panel name="scroll_content_panel">
+ <panel name="scroll_content_panel2">
<texture_picker label="Haare" name="hair-baked"/>
<texture_picker label="Haare" name="hair_grain"/>
<texture_picker label="Alpha: Haare" name="hair_alpha"/>
diff --git a/indra/newview/skins/default/xui/en/floater_avatar_textures.xml b/indra/newview/skins/default/xui/en/floater_avatar_textures.xml
index 09026884b9..831368f4de 100644
--- a/indra/newview/skins/default/xui/en/floater_avatar_textures.xml
+++ b/indra/newview/skins/default/xui/en/floater_avatar_textures.xml
@@ -70,13 +70,13 @@ Textures
width="150" />
<panel
- name="scroll_content_panel"
+ name="scroll_content_panel2"
follows="left|top"
min_height="300"
layout="topleft"
- top="43"
+ top="60"
background_visible="false"
- height="930"
+ height="1113"
left="0"
width="1230">
@@ -86,7 +86,7 @@ Textures
layout="topleft"
left="10"
name="hair-baked"
- top="17"
+ top="3"
width="92" />
<texture_picker
height="103"
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 607c7698c3..15dacddb80 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -2811,7 +2811,7 @@ function="World.EnvPreset"
function="Advanced.ForceErrorOSException" />
<menu_item_call.on_visible
function="Advanced.EnableErrorOSException" />
- </menu_item_call>
+ </menu_item_call>
<menu_item_call
label="Force a Crash in a Coroutine"
name="Force a Crash in a Coroutine">
@@ -2819,6 +2819,18 @@ function="World.EnvPreset"
function="Advanced.ForceErrorCoroutineCrash" />
</menu_item_call>
<menu_item_call
+ label="Force a Crash in a Coroprocedure"
+ name="Force a Crash in a Coroprocedure">
+ <menu_item_call.on_click
+ function="Advanced.ForceErrorCoroprocedureCrash" />
+ </menu_item_call>
+ <menu_item_call
+ label="Force a Crash in a Work Queue"
+ name="Force a Crash in a Work Queue">
+ <menu_item_call.on_click
+ function="Advanced.ForceErrorWorkQueueCrash" />
+ </menu_item_call>
+ <menu_item_call
label="Force a Crash in a Thread"
name="Force a Crash in a Thread">
<menu_item_call.on_click
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index b035360206..4439e476a2 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -12592,4 +12592,16 @@ Select the "use as favorite folder" from a folder's menu to set it as the favori
name="okbutton"
yestext="OK"/>
</notification>
+
+ <notification
+ icon="alertmodal.tga"
+ name="WaterExclusionSurfacesWarning"
+ type="alertmodal">
+ Checking the hide water box will overwrite the texture, bumpiness, and shininess choices.
+ <tag>confirm</tag>
+ <usetemplate
+ name="okcancelbuttons"
+ notext="Cancel"
+ yestext="Continue"/>
+ </notification>
</notifications>
diff --git a/indra/newview/skins/default/xui/en/panel_tools_texture.xml b/indra/newview/skins/default/xui/en/panel_tools_texture.xml
index e80f315259..9a19d06432 100644
--- a/indra/newview/skins/default/xui/en/panel_tools_texture.xml
+++ b/indra/newview/skins/default/xui/en/panel_tools_texture.xml
@@ -123,6 +123,14 @@
name="checkbox fullbright"
top_pad="4"
width="81" />
+ <check_box
+ height="19"
+ label="Hide water"
+ layout="topleft"
+ left="172"
+ top_delta="0"
+ name="checkbox_hide_water"
+ width="81" />
<view_border
bevel_style="none"
follows="top|left"
diff --git a/indra/newview/skins/default/xui/es/floater_avatar_textures.xml b/indra/newview/skins/default/xui/es/floater_avatar_textures.xml
index 0a52a70ff1..b043662975 100644
--- a/indra/newview/skins/default/xui/es/floater_avatar_textures.xml
+++ b/indra/newview/skins/default/xui/es/floater_avatar_textures.xml
@@ -6,7 +6,7 @@
<text name="label">Texturas obtenidas mediante bake</text>
<text name="composite_label">Texturas compuestas</text>
<button label="Volcar IDs a la consola" label_selected="Volcado" name="Dump"/>
- <panel name="scroll_content_panel">
+ <panel name="scroll_content_panel2">
<texture_picker label="Pelo" name="hair-baked"/>
<texture_picker label="Pelo" name="hair_grain"/>
<texture_picker label="Alfa del pelo" name="hair_alpha"/>
diff --git a/indra/newview/skins/default/xui/fr/floater_avatar_textures.xml b/indra/newview/skins/default/xui/fr/floater_avatar_textures.xml
index a1f6d85836..e910632e50 100644
--- a/indra/newview/skins/default/xui/fr/floater_avatar_textures.xml
+++ b/indra/newview/skins/default/xui/fr/floater_avatar_textures.xml
@@ -6,7 +6,7 @@
<text name="label">Textures figées</text>
<text name="composite_label">Textures composites</text>
<button label="Vider les ID dans la console" label_selected="Vider" name="Dump"/>
- <panel name="scroll_content_panel">
+ <panel name="scroll_content_panel2">
<texture_picker label="Cheveux" name="hair-baked"/>
<texture_picker label="Cheveux" name="hair_grain"/>
<texture_picker label="Alpha cheveux" name="hair_alpha"/>
diff --git a/indra/newview/skins/default/xui/it/floater_avatar_textures.xml b/indra/newview/skins/default/xui/it/floater_avatar_textures.xml
index 288b0a2fea..c0e56805f3 100644
--- a/indra/newview/skins/default/xui/it/floater_avatar_textures.xml
+++ b/indra/newview/skins/default/xui/it/floater_avatar_textures.xml
@@ -8,7 +8,7 @@ texture</text>
<text name="composite_label">Composito
Texture</text>
<button label="Memorizza gli ID sulla console" label_selected="Dump" name="Dump"/>
- <panel name="scroll_content_panel">
+ <panel name="scroll_content_panel2">
<texture_picker label="Capigliature" name="hair-baked"/>
<texture_picker label="Capigliature" name="hair_grain"/>
<texture_picker label="Alpha dei capelli" name="hair_alpha"/>
diff --git a/indra/newview/skins/default/xui/ja/floater_avatar_textures.xml b/indra/newview/skins/default/xui/ja/floater_avatar_textures.xml
index e71ed5c599..5de3486ea1 100644
--- a/indra/newview/skins/default/xui/ja/floater_avatar_textures.xml
+++ b/indra/newview/skins/default/xui/ja/floater_avatar_textures.xml
@@ -13,7 +13,7 @@
合成テクスチャ
</text>
<button label="IDをコンソールにダンプ" label_selected="ダンプ" name="Dump"/>
- <panel name="scroll_content_panel">
+ <panel name="scroll_content_panel2">
<texture_picker label="髪" name="hair-baked"/>
<texture_picker label="髪" name="hair_grain"/>
<texture_picker label="髪のアルファ" name="hair_alpha"/>
diff --git a/indra/newview/skins/default/xui/pl/floater_avatar_textures.xml b/indra/newview/skins/default/xui/pl/floater_avatar_textures.xml
index 18f5a40ea5..ee5e4d277c 100644
--- a/indra/newview/skins/default/xui/pl/floater_avatar_textures.xml
+++ b/indra/newview/skins/default/xui/pl/floater_avatar_textures.xml
@@ -14,7 +14,7 @@ prerenderowane
kompozytowe
</text>
<button label="Zrzuć ID do Konsoli" label_selected="Zrzuć" name="Dump" />
- <panel name="scroll_content_panel">
+ <panel name="scroll_content_panel2">
<texture_picker label="Włosy" name="hair-baked" />
<texture_picker label="Włosy" name="hair_grain" />
<texture_picker label="Alpha włosów" name="hair_alpha" />
diff --git a/indra/newview/skins/default/xui/pt/floater_avatar_textures.xml b/indra/newview/skins/default/xui/pt/floater_avatar_textures.xml
index 6cdf66cb14..3a0c2febd7 100644
--- a/indra/newview/skins/default/xui/pt/floater_avatar_textures.xml
+++ b/indra/newview/skins/default/xui/pt/floater_avatar_textures.xml
@@ -8,7 +8,7 @@ Texturas</text>
<text name="composite_label">Compósito:
Texturas</text>
<button label="Enviar IDs para painel" label_selected="Dump" name="Dump"/>
- <panel name="scroll_content_panel">
+ <panel name="scroll_content_panel2">
<texture_picker label="Cabelo" name="hair-baked"/>
<texture_picker label="Cabelo" name="hair_grain"/>
<texture_picker label="Cabelo alpha" name="hair_alpha"/>
diff --git a/indra/newview/skins/default/xui/ru/floater_avatar_textures.xml b/indra/newview/skins/default/xui/ru/floater_avatar_textures.xml
index cb39f66247..b8906eebbd 100644
--- a/indra/newview/skins/default/xui/ru/floater_avatar_textures.xml
+++ b/indra/newview/skins/default/xui/ru/floater_avatar_textures.xml
@@ -14,7 +14,7 @@
Текстуры
</text>
<button label="Вывод ID на консоль" label_selected="Вывод" name="Dump"/>
- <panel name="scroll_content_panel">
+ <panel name="scroll_content_panel2">
<texture_picker label="Волосы" name="hair-baked"/>
<texture_picker label="Волосы" name="hair_grain"/>
<texture_picker label="Альфа волос" name="hair_alpha"/>
diff --git a/indra/newview/skins/default/xui/tr/floater_avatar_textures.xml b/indra/newview/skins/default/xui/tr/floater_avatar_textures.xml
index b519cfa1a9..2dabab2bca 100644
--- a/indra/newview/skins/default/xui/tr/floater_avatar_textures.xml
+++ b/indra/newview/skins/default/xui/tr/floater_avatar_textures.xml
@@ -14,7 +14,7 @@ Dokular
Dokular
</text>
<button label="Kimlikleri Konsole Yığ" label_selected="Yığ" name="Dump"/>
- <panel name="scroll_content_panel">
+ <panel name="scroll_content_panel2">
<texture_picker label="Saç" name="hair-baked"/>
<texture_picker label="Saç" name="hair_grain"/>
<texture_picker label="Saç Alfası" name="hair_alpha"/>
diff --git a/indra/newview/skins/default/xui/zh/floater_avatar_textures.xml b/indra/newview/skins/default/xui/zh/floater_avatar_textures.xml
index 69309c96de..260216fb15 100644
--- a/indra/newview/skins/default/xui/zh/floater_avatar_textures.xml
+++ b/indra/newview/skins/default/xui/zh/floater_avatar_textures.xml
@@ -14,7 +14,7 @@
材質
</text>
<button label="傾印 ID 到控制臺" label_selected="傾印" name="Dump"/>
- <panel name="scroll_content_panel">
+ <panel name="scroll_content_panel2">
<texture_picker label="頭髮" name="hair-baked"/>
<texture_picker label="頭髮" name="hair_grain"/>
<texture_picker label="頭髮半透明" name="hair_alpha"/>