Merge branch 'release/2025.08' of https://github.com/secondlife/viewer

# Conflicts:
#	indra/newview/llfloatermarketplace.cpp
#	indra/newview/llviewermessage.cpp
#	indra/newview/skins/default/xui/en/panel_preferences_move.xml
master
Ansariel 2025-11-17 23:37:55 +01:00
commit 51f7e7a7d7
22 changed files with 230 additions and 57 deletions

View File

@ -80,7 +80,7 @@ void LLQueuedThread::shutdown()
mRequestQueue.close();
}
S32 timeout = 100;
S32 timeout = 50;
for ( ; timeout>0; timeout--)
{
if (isStopped())
@ -101,19 +101,34 @@ void LLQueuedThread::shutdown()
}
QueuedRequest* req;
S32 active_count = 0;
S32 queued_count = 0;
bool has_active = false;
lockData();
while ( (req = (QueuedRequest*)mRequestHash.pop_element()) )
{
if (req->getStatus() == STATUS_QUEUED || req->getStatus() == STATUS_INPROGRESS)
if (req->getStatus() == STATUS_INPROGRESS)
{
++active_count;
has_active = true;
req->setFlags(FLAG_ABORT | FLAG_AUTO_COMPLETE);
continue;
}
if (req->getStatus() == STATUS_QUEUED)
{
++queued_count;
req->setStatus(STATUS_ABORTED); // avoid assert in deleteRequest
}
req->deleteRequest();
}
if (active_count)
unlockData();
if (queued_count)
{
LL_WARNS() << "~LLQueuedThread() called with active requests: " << active_count << LL_ENDL;
LL_WARNS() << "~LLQueuedThread() called with unpocessed requests: " << queued_count << LL_ENDL;
}
if (has_active)
{
LL_WARNS() << "~LLQueuedThread() called with active requests!" << LL_ENDL;
ms_sleep(100); // last chance for request to finish
printQueueStats();
}
mRequestQueue.close();
@ -600,7 +615,12 @@ LLQueuedThread::QueuedRequest::QueuedRequest(LLQueuedThread::handle_t handle, U3
LLQueuedThread::QueuedRequest::~QueuedRequest()
{
llassert_always(mStatus == STATUS_DELETE);
if (mStatus != STATUS_DELETE)
{
// The only method to delete a request is deleteRequest(),
// it should have set the status to STATUS_DELETE
LL_ERRS() << "LLQueuedThread::QueuedRequest deleted with status " << mStatus << LL_ENDL;
}
}
//virtual

View File

@ -1010,15 +1010,18 @@ void LLPluginProcessParent::poll(F64 timeout)
// </FS:Beq>
}
// Remove instances in the done state from the sInstances map.
LLCoros::LockType lock(*sInstancesMutex);
mapInstances_t::iterator itClean = sInstances.begin();
while (itClean != sInstances.end())
if (sInstancesMutex)
{
if ((*itClean).second->isDone())
itClean = sInstances.erase(itClean);
else
++itClean;
// Remove instances in the done state from the sInstances map.
LLCoros::LockType lock(*sInstancesMutex);
mapInstances_t::iterator itClean = sInstances.begin();
while (itClean != sInstances.end())
{
if ((*itClean).second->isDone())
itClean = sInstances.erase(itClean);
else
++itClean;
}
}
}

View File

@ -57,6 +57,7 @@ S32 LLGLSLShader::sIndexedTextureChannels = 0;
U32 LLGLSLShader::sMaxGLTFMaterials = 0;
U32 LLGLSLShader::sMaxGLTFNodes = 0;
bool LLGLSLShader::sProfileEnabled = false;
bool LLGLSLShader::sCanProfile = true;
std::set<LLGLSLShader*> LLGLSLShader::sInstances;
LLGLSLShader::defines_map_t LLGLSLShader::sGlobalDefines;
U64 LLGLSLShader::sTotalTimeElapsed = 0;
@ -267,7 +268,7 @@ void LLGLSLShader::placeProfileQuery(bool for_runtime)
bool LLGLSLShader::readProfileQuery(bool for_runtime, bool force_read)
{
if (sProfileEnabled || for_runtime)
if ((sProfileEnabled || for_runtime) && sCanProfile)
{
if (!mProfilePending)
{

View File

@ -160,6 +160,7 @@ public:
static std::set<LLGLSLShader*> sInstances;
static bool sProfileEnabled;
static bool sCanProfile;
LLGLSLShader();
~LLGLSLShader();

View File

@ -479,7 +479,34 @@ void LLAccordionCtrlTab::onUpdateScrollToChild(const LLUICtrl *cntrl)
// Translate to parent coordinatess to check if we are in visible rectangle
rect.translate(getRect().mLeft, getRect().mBottom);
if (!getRect().contains(rect))
bool needs_to_scroll = false;
const LLRect &acc_rect = getRect();
if (!acc_rect.contains(rect))
{
if (acc_rect.mTop < rect.mBottom || acc_rect.mBottom > rect.mTop)
{
// Content fully not in view
needs_to_scroll = true;
}
else if (acc_rect.getHeight() >= rect.getHeight())
{
// Content can be displayed fully, but only partially in view
needs_to_scroll = true;
}
else if (acc_rect.mTop <= rect.mTop || acc_rect.mBottom >= rect.mBottom)
{
// Intersects, but too big to be displayed fully
S32 covered_height = acc_rect.mTop > rect.mTop ? rect.mTop - acc_rect.mBottom : acc_rect.mTop - rect.mBottom;
constexpr F32 covered_ratio = 0.7f;
if (covered_height < covered_ratio * acc_rect.getHeight())
{
// Try to show bigger portion of the content
needs_to_scroll = true;
}
}
// else too big and in the middle of the view as is
}
if (needs_to_scroll)
{
// for accordition's scroll, height is in pixels
// Back to local coords and calculate position for scroller

View File

@ -1184,8 +1184,37 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s
static LLUICachedControl<bool> useBWEmojis("FSUseBWEmojis", false); // <FS:Beq/> Add B&W emoji font support
LLStyleSP emoji_style;
LLEmojiDictionary* ed = LLEmojiDictionary::instanceExists() ? LLEmojiDictionary::getInstance() : NULL;
LLTextSegment* segmentp = nullptr;
segment_vec_t::iterator seg_iter;
if (segments && segments->size() > 0)
{
seg_iter = segments->begin();
segmentp = *seg_iter;
}
for (S32 text_kitty = 0, text_len = static_cast<S32>(wstr.size()); text_kitty < text_len; text_kitty++)
{
if (segmentp)
{
if (segmentp->getEnd() <= pos + text_kitty)
{
seg_iter++;
if (seg_iter != segments->end())
{
segmentp = *seg_iter;
}
else
{
segmentp = nullptr;
}
}
if (segmentp && !segmentp->getPermitsEmoji())
{
// Some segments, like LLInlineViewSegment do not permit splitting
// and should not be interrupted by emoji segments
continue;
}
}
llwchar code = wstr[text_kitty];
bool isEmoji = ed ? ed->isEmoji(code) : LLStringOps::isEmoji(code);
if (isEmoji)
@ -3801,6 +3830,7 @@ S32 LLTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offs
void LLTextSegment::updateLayout(const LLTextBase& editor) {}
F32 LLTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect) { return draw_rect.mLeft; }
bool LLTextSegment::canEdit() const { return false; }
bool LLTextSegment::getPermitsEmoji() const { return true; }
void LLTextSegment::unlinkFromDocument(LLTextBase*) {}
void LLTextSegment::linkToDocument(LLTextBase*) {}
const LLUIColor& LLTextSegment::getColor() const { static const LLUIColor white = LLUIColorTable::instance().getColor("White", LLColor4::white); return white; }

View File

@ -87,6 +87,7 @@ public:
virtual void updateLayout(const class LLTextBase& editor);
virtual F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect);
virtual bool canEdit() const;
virtual bool getPermitsEmoji() const;
virtual void unlinkFromDocument(class LLTextBase* editor);
virtual void linkToDocument(class LLTextBase* editor);
@ -255,6 +256,7 @@ public:
/*virtual*/ void updateLayout(const class LLTextBase& editor);
/*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect);
/*virtual*/ bool canEdit() const { return false; }
/*virtual*/ bool getPermitsEmoji() const { return false; }
/*virtual*/ void unlinkFromDocument(class LLTextBase* editor);
/*virtual*/ void linkToDocument(class LLTextBase* editor);

View File

@ -186,7 +186,7 @@ std::map<S32, std::string> LLTeleportRequest::sTeleportStatusName = { { kPending
class LLTeleportRequestViaLandmark : public LLTeleportRequest
{
public:
LLTeleportRequestViaLandmark(const LLUUID &pLandmarkId);
LLTeleportRequestViaLandmark(const LLUUID &pLandmarkId, bool log = true);
virtual ~LLTeleportRequestViaLandmark();
virtual void toOstream(std::ostream& os) const;
@ -198,6 +198,7 @@ public:
protected:
inline const LLUUID &getLandmarkId() const {return mLandmarkId;};
bool mLogOnDestruction = true;
private:
LLUUID mLandmarkId;
@ -6157,16 +6158,25 @@ void LLTeleportRequest::toOstream(std::ostream& os) const
//-----------------------------------------------------------------------------
// LLTeleportRequestViaLandmark
//-----------------------------------------------------------------------------
LLTeleportRequestViaLandmark::LLTeleportRequestViaLandmark(const LLUUID &pLandmarkId)
: LLTeleportRequest(),
mLandmarkId(pLandmarkId)
LLTeleportRequestViaLandmark::LLTeleportRequestViaLandmark(const LLUUID &pLandmarkId, bool log)
: LLTeleportRequest()
, mLandmarkId(pLandmarkId)
, mLogOnDestruction(true)
{
LL_INFOS("Teleport") << "LLTeleportRequestViaLandmark created, " << *this << LL_ENDL;
if (log)
{
// Workaround to not log twice for LLTeleportRequestViaLure, besides this wouldn't have logged fully.
LL_INFOS("Teleport") << "LLTeleportRequestViaLandmark created, " << *this << LL_ENDL;
}
}
LLTeleportRequestViaLandmark::~LLTeleportRequestViaLandmark()
{
LL_INFOS("Teleport") << "~LLTeleportRequestViaLandmark, " << *this << LL_ENDL;
if (mLogOnDestruction)
{
// Workaround to not crash on toOstream for derived classes and to not log twice.
LL_INFOS("Teleport") << "~LLTeleportRequestViaLandmark, " << *this << LL_ENDL;
}
}
void LLTeleportRequestViaLandmark::toOstream(std::ostream& os) const
@ -6196,16 +6206,20 @@ void LLTeleportRequestViaLandmark::restartTeleport()
// LLTeleportRequestViaLure
//-----------------------------------------------------------------------------
LLTeleportRequestViaLure::LLTeleportRequestViaLure(const LLUUID &pLureId, bool pIsLureGodLike)
: LLTeleportRequestViaLandmark(pLureId),
LLTeleportRequestViaLure::LLTeleportRequestViaLure(const LLUUID& pLureId, bool pIsLureGodLike)
: LLTeleportRequestViaLandmark(pLureId, false),
mIsLureGodLike(pIsLureGodLike)
{
LL_INFOS("Teleport") << "LLTeleportRequestViaLure created" << LL_ENDL;
LL_INFOS("Teleport") << "LLTeleportRequestViaLure created: " << *this << LL_ENDL;
}
LLTeleportRequestViaLure::~LLTeleportRequestViaLure()
{
LL_INFOS("Teleport") << "~LLTeleportRequestViaLure" << LL_ENDL;
if (mLogOnDestruction)
{
LL_INFOS("Teleport") << "~LLTeleportRequestViaLure: " << *this << LL_ENDL;
mLogOnDestruction = false;
}
}
void LLTeleportRequestViaLure::toOstream(std::ostream& os) const

View File

@ -245,7 +245,14 @@ namespace
// If marker doesn't exist, create a marker with 'other' code for next launch
// otherwise don't override existing file
// Any unmarked crashes will be considered as freezes
app->createErrorMarker(LAST_EXEC_OTHER_CRASH);
if (app->logoutRequestSent())
{
app->createErrorMarker(LAST_EXEC_LOGOUT_CRASH);
}
else
{
app->createErrorMarker(LAST_EXEC_OTHER_CRASH);
}
}
} // MDSCB_EXCEPTIONCODE

View File

@ -409,8 +409,53 @@ F32 logExceptionBenchmark()
}
#endif
bool checkRDNA35()
{
// This checks if we're running on an RDNA3.5 GPU. You're only going to see these on AMD's APUs.
// As of driver version 25, we're seeing stalls in some of our queries.
// This appears to be a driver bug, and appears to be specific RDNA3.5 APUs.
// There's multiples of these guys, so we just use this function to check if that GPU is on the list of known RDNA3.5 APUs.
// - Geenz 11/12/2025
std::array<std::string, 7> rdna35GPUs = {
"8060S",
"8050S",
"8040S",
"860M",
"840M",
"890M",
"880M"
};
for (const auto& gpu_name : rdna35GPUs)
{
if (gGLManager.getRawGLString().find(gpu_name) != std::string::npos)
{
LL_WARNS("RenderInit") << "Detected AMD RDNA3.5 GPU (" << gpu_name << ")." << LL_ENDL;
return true;
}
}
return false;
}
bool LLFeatureManager::loadGPUClass()
{
// This is a hack for certain AMD GPUs in newer driver versions on certain APUs.
// These GPUs will show inconsistent freezes when attempting to run shader profiles against them.
// This is extremely problematic as it can lead to:
// - Login freezes
// - Inability to start the client
// - Completely random avatars triggering a freeze
// As a result, we filter out these GPUs for shader profiling.
// - Geenz 11/11/2025
if (gGLManager.getRawGLString().find("Radeon") != std::string::npos && checkRDNA35() && gGLManager.mDriverVersionVendorString.find("25.") != std::string::npos)
{
LL_WARNS("RenderInit") << "Detected AMD RDNA3.5 GPU on a known bad driver; disabling benchmark and occlusion culling to prevent freezes." << LL_ENDL;
gSavedSettings.setBOOL("SkipBenchmark", true);
gSavedSettings.setBOOL("UseOcclusion", false);
}
if (!gSavedSettings.getBOOL("SkipBenchmark"))
{
F32 class1_gbps = gSavedSettings.getF32("RenderClass1MemoryBandwidth");

View File

@ -123,6 +123,7 @@ public:
S32 getVersion() const { return mTableVersion; }
void setSafe(const bool safe) { mSafe = safe; }
bool isSafe() const { return mSafe; }
bool skipProfiling() const { return mSkipProfiling; }
LLFeatureList *findMask(const std::string& name);
bool maskFeatures(const std::string& name);
@ -170,6 +171,7 @@ protected:
F32 mExpectedGLVersion; //expected GL version according to gpu table
std::string mGPUString;
bool mGPUSupported;
bool mSkipProfiling = false;
};
inline

View File

@ -46,11 +46,9 @@ void LLFloaterMarketplace::onClose(bool app_quitting)
bool LLFloaterMarketplace::postBuild()
{
LLFloaterWebContent::postBuild();
// <FS:Ansariel> Already set in LLFloaterWebContent
//mWebBrowser = getChild<LLMediaCtrl>("marketplace_contents");
//mWebBrowser->addObserver(this);
// </FS:Ansariel>
if (!LLFloaterWebContent::postBuild())
return false;
mWebBrowser->setErrorPageURL(gSavedSettings.getString("GenericErrorPageURL"));
std::string url = gSavedSettings.getString("MarketplaceURL");
mWebBrowser->navigateTo(url, HTTP_CONTENT_TEXT_HTML);

View File

@ -289,10 +289,12 @@ bool LLFloaterRegionInfo::postBuild()
static LLCachedControl<bool> feature_pbr_terrain_transforms_enabled(gSavedSettings, "RenderTerrainPBRTransformsEnabled", false);
if (!feature_pbr_terrain_transforms_enabled() || !feature_pbr_terrain_enabled())
{
LL_INFOS("Terrain") << "Building region terrain panel from panel_region_terrain.xml" << LL_ENDL;
panel->buildFromFile("panel_region_terrain.xml");
}
else
{
LL_INFOS("Terrain") << "Building region terrain panel from panel_region_terrain_texture_transform.xml" << LL_ENDL;
panel->buildFromFile("panel_region_terrain_texture_transform.xml");
}
mTab->addTabPanel(panel);
@ -1676,6 +1678,11 @@ bool LLPanelRegionTerrainInfo::validateMaterials()
const LLUUID& material_asset_id = material_ctrl->getImageAssetID();
llassert(material_asset_id.notNull());
if (material_asset_id.isNull()) { return false; }
if (material_asset_id == BLANK_MATERIAL_ASSET_ID)
{
// Default/Blank material is valid by default
continue;
}
const LLFetchedGLTFMaterial* material = gGLTFMaterialList.getMaterial(material_asset_id);
if (!material->isLoaded())
{
@ -2194,18 +2201,7 @@ void LLPanelRegionTerrainInfo::initMaterialCtrl(LLTextureCtrl*& ctrl, const std:
ctrl->setCommitCallback(
[this, index](LLUICtrl* ctrl, const LLSD& param)
{
if (!mMaterialScaleUCtrl[index]
|| !mMaterialScaleVCtrl[index]
|| !mMaterialRotationCtrl[index]
|| !mMaterialOffsetUCtrl[index]
|| !mMaterialOffsetVCtrl[index]) return;
mMaterialScaleUCtrl[index]->setValue(1.f);
mMaterialScaleVCtrl[index]->setValue(1.f);
mMaterialRotationCtrl[index]->setValue(0.f);
mMaterialOffsetUCtrl[index]->setValue(0.f);
mMaterialOffsetVCtrl[index]->setValue(0.f);
onChangeAnything();
callbackMaterialCommit(index);
});
}
@ -2333,6 +2329,25 @@ bool LLPanelRegionTerrainInfo::callbackBakeTerrain(const LLSD& notification, con
return false;
}
void LLPanelRegionTerrainInfo::callbackMaterialCommit(S32 index)
{
// These can be null if 'transforms' panel was not inited
if (mMaterialScaleUCtrl[index]
&& mMaterialScaleVCtrl[index]
&& mMaterialRotationCtrl[index]
&& mMaterialOffsetUCtrl[index]
&& mMaterialOffsetVCtrl[index])
{
mMaterialScaleUCtrl[index]->setValue(1.f);
mMaterialScaleVCtrl[index]->setValue(1.f);
mMaterialRotationCtrl[index]->setValue(0.f);
mMaterialOffsetUCtrl[index]->setValue(0.f);
mMaterialOffsetVCtrl[index]->setValue(0.f);
}
onChangeAnything();
}
/////////////////////////////////////////////////////////////////////////////
// LLPanelEstateInfo
//

View File

@ -302,6 +302,7 @@ public:
static void onClickBakeTerrain(void*);
bool callbackBakeTerrain(const LLSD& notification, const LLSD& response);
bool callbackTextureHeights(const LLSD& notification, const LLSD& response);
void callbackMaterialCommit(S32 index);
protected:
bool sendUpdate() override;

View File

@ -179,15 +179,14 @@ void LLFloaterSearch::initiateSearch(const LLSD& tokens)
// Naviation to the calculated URL - we know it's HTML so we can
// tell the media system not to bother with the MIME type check.
LLMediaCtrl* search_browser = findChild<LLMediaCtrl>("search_contents");
search_browser->navigateTo(url, HTTP_CONTENT_TEXT_HTML);
mWebBrowser->navigateTo(url, HTTP_CONTENT_TEXT_HTML);
}
bool LLFloaterSearch::postBuild()
{
LLFloaterWebContent::postBuild();
mWebBrowser = getChild<LLMediaCtrl>("search_contents");
mWebBrowser->addObserver(this);
if (!LLFloaterWebContent::postBuild())
return false;
mWebBrowser->setErrorPageURL(gSavedSettings.getString("GenericErrorPageURL"));
// If cookie is there, will set it now, Otherwise will have to wait for login completion

View File

@ -2217,7 +2217,6 @@ bool LLSelectMgr::selectionSetGLTFMaterial(const LLUUID& mat_id)
objectp->clearTEWaterExclusion(te);
// Blank out most override data on the object and send to server
objectp->setRenderMaterialID(te, asset_id);
if (should_preserve_transforms && preserved_override)
{
// Apply material with preserved transforms

View File

@ -1551,7 +1551,7 @@ void LLToolDragAndDrop::dropMaterialAllFaces(LLViewerObject* hit_obj,
}
else
{
hit_obj->setRenderMaterialID(te, asset_id, false, true);
hit_obj->setRenderMaterialID(te, asset_id);
}
}
}

View File

@ -267,8 +267,11 @@ void display_stats()
if (gRecentFPSTime.getElapsedTimeF32() >= FPS_LOG_FREQUENCY)
{
LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("DS - FPS");
LLTrace::Recording& recording = LLTrace::get_frame_recording().getLastRecording();
F64 normalized_session_jitter = recording.getLastValue(LLStatViewer::NOTRMALIZED_FRAMETIME_JITTER_SESSION);
F64 normalized_period_jitter = recording.getLastValue(LLStatViewer::NORMALIZED_FRAMTIME_JITTER_PERIOD);
F32 fps = gRecentFrameCount / FPS_LOG_FREQUENCY;
LL_INFOS() << llformat("FPS: %.02f", fps) << LL_ENDL;
LL_INFOS() << llformat("FPS: %.02f SESSION JITTER: %.4f PERIOD JITTER: %.4f", fps, normalized_session_jitter, normalized_period_jitter) << LL_ENDL;
gRecentFrameCount = 0;
gRecentFPSTime.reset();
}

View File

@ -1372,8 +1372,8 @@ void LLViewerMedia::getOpenIDCookieCoro(std::string url)
std::string browser_name;
};
struct MediaCookieInstance media_cookie_instances[] = {
{"search", "search_contents" },
{"marketplace", "marketplace_contents" },
{"search", "webbrowser" },
{"marketplace", "webbrowser" },
{"destinations", "destination_guide_contents" },
};
for (MediaCookieInstance mci : media_cookie_instances)

View File

@ -4029,7 +4029,10 @@ void process_crossed_region(LLMessageSystem* msg, void**)
return;
}
LL_INFOS("Messaging") << "process_crossed_region()" << LL_ENDL;
gAgentAvatarp->resetRegionCrossingTimer();
if (isAgentAvatarValid())
{
gAgentAvatarp->resetRegionCrossingTimer();
}
// <FS:Ansariel> FIRE-12004: Attachments getting lost on TP; this is apparently the place to
// hook in for region crossings - we get an info from the simulator that we
// crossed a region and then the viewer starts the handover process. We only

View File

@ -234,6 +234,9 @@ extern LLTrace::EventStatHandle<F64Seconds > AVATAR_EDIT_TIME,
extern LLTrace::EventStatHandle<LLUnit<F32, LLUnits::Percent> > OBJECT_CACHE_HIT_RATE;
extern LLTrace::SampleStatHandle<F64> NOTRMALIZED_FRAMETIME_JITTER_SESSION;
extern LLTrace::SampleStatHandle<F64> NORMALIZED_FRAMTIME_JITTER_PERIOD;
}
class LLViewerStats : public LLSingleton<LLViewerStats>

View File

@ -170,7 +170,7 @@
layout="topleft"
left="0"
trusted_content="true"
name="search_contents"
name="webbrowser"
top="0"/>
</layout_panel>
<layout_panel name="status_bar"