Merge branch 'release/2025.03' into rye/forevermac

master
Jonathan "Geenz" Goodman 2025-03-11 22:44:49 -04:00
commit e0d14e02e1
174 changed files with 3351 additions and 1772 deletions

View File

@ -42,7 +42,7 @@ jobs:
needs: setup
strategy:
matrix:
runner: [windows-large, macos-12-large]
runner: [windows-large, macos-15-xlarge]
configuration: ${{ fromJSON(needs.setup.outputs.configurations) }}
runs-on: ${{ matrix.runner }}
outputs:
@ -64,7 +64,7 @@ jobs:
# autobuild-package.xml.
AUTOBUILD_VCS_INFO: "true"
AUTOBUILD_VSVER: "170"
DEVELOPER_DIR: "/Applications/Xcode_14.0.1.app/Contents/Developer"
DEVELOPER_DIR: "/Applications/Xcode_16.1.app/Contents/Developer"
# Ensure that Linden viewer builds engage Bugsplat.
BUGSPLAT_DB: ${{ needs.setup.outputs.bugsplat_db }}
build_coverity: false

View File

@ -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

View File

@ -31,6 +31,11 @@ else()
set( USE_AUTOBUILD_3P ON )
endif()
if (NOT DEFINED CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 20)
endif()
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(Variables)
include(BuildVersion)

View File

@ -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)

View File

@ -981,7 +981,7 @@ void LLPolyMesh::initializeForMorph()
LLVector4a::memcpyNonAliased16((F32*) mScaledNormals, (F32*) mSharedData->mBaseNormals, sizeof(LLVector4a) * mSharedData->mNumVertices);
LLVector4a::memcpyNonAliased16((F32*) mBinormals, (F32*) mSharedData->mBaseNormals, sizeof(LLVector4a) * mSharedData->mNumVertices);
LLVector4a::memcpyNonAliased16((F32*) mScaledBinormals, (F32*) mSharedData->mBaseNormals, sizeof(LLVector4a) * mSharedData->mNumVertices);
LLVector4a::memcpyNonAliased16((F32*) mTexCoords, (F32*) mSharedData->mTexCoords, sizeof(LLVector2) * (mSharedData->mNumVertices + mSharedData->mNumVertices%2));
memcpy((F32*) mTexCoords, (F32*) mSharedData->mTexCoords, sizeof(LLVector2) * (mSharedData->mNumVertices)); // allocated in LLPolyMeshSharedData::allocateVertexData
for (S32 i = 0; i < mSharedData->mNumVertices; ++i)
{

View File

@ -79,11 +79,8 @@ public:
// shadow base-class string() method with UTF-8 aware method
std::string string() const
{
// Short of forbidden type punning, I see no way to avoid copying this
// std::u8string to a std::string.
auto u8str{ super::u8string() };
// from https://github.com/tahonermann/char8_t-remediation/blob/master/char8_t-remediation.h#L180-L182
return { u8str.begin(), u8str.end() };
auto u8 = super::u8string();
return std::string(u8.begin(), u8.end());
}
// On Posix systems, where value_type is already char, this operator
// std::string() method shadows the base class operator string_type()

View File

@ -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:

View File

@ -310,18 +310,25 @@ namespace
static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific
U32 exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop)
U32 exception_filter(U32 code, struct _EXCEPTION_POINTERS* exception_infop)
{
if (code == STATUS_MSC_EXCEPTION)
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
// handle it, convert to std::exception
return EXCEPTION_EXECUTE_HANDLER;
}
return EXCEPTION_CONTINUE_SEARCH;
}
void sehandle(const LLCoros::callable_t& callable)
@ -379,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
@ -387,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

View File

@ -77,6 +77,15 @@ std::string LLDate::asRFC1123() const
return toHTTPDateString (std::string ("%A, %d %b %Y %H:%M:%S GMT"));
}
std::string LLDate::toLocalDateString (std::string fmt) const
{
LL_PROFILE_ZONE_SCOPED;
time_t locSeconds = (time_t) mSecondsSinceEpoch;
struct tm * lt = localtime (&locSeconds);
return toHTTPDateString(lt, fmt);
}
std::string LLDate::toHTTPDateString (std::string fmt) const
{
LL_PROFILE_ZONE_SCOPED;

View File

@ -77,6 +77,7 @@ public:
std::string asRFC1123() const;
void toStream(std::ostream&) const;
bool split(S32 *year, S32 *month = NULL, S32 *day = NULL, S32 *hour = NULL, S32 *min = NULL, S32 *sec = NULL) const;
std::string toLocalDateString(std::string fmt) const;
std::string toHTTPDateString (std::string fmt) const;
static std::string toHTTPDateString (tm * gmt, std::string fmt);
/**

View File

@ -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);

View File

@ -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)

View File

@ -137,7 +137,8 @@ const std::string LLSettingsSky::SETTING_REFLECTION_PROBE_AMBIANCE("reflection_p
const LLUUID LLSettingsSky::DEFAULT_ASSET_ID("651510b8-5f4d-8991-1592-e7eeab2a5a06");
F32 LLSettingsSky::sAutoAdjustProbeAmbiance = 1.f;
const F32 LLSettingsSky::DEFAULT_AUTO_ADJUST_PROBE_AMBIANCE = 1.f;
F32 LLSettingsSky::sAutoAdjustProbeAmbiance = DEFAULT_AUTO_ADJUST_PROBE_AMBIANCE;
static const LLUUID DEFAULT_SUN_ID("32bfbcea-24b1-fb9d-1ef9-48a28a63730f"); // dataserver
static const LLUUID DEFAULT_MOON_ID("d07f6eed-b96a-47cd-b51d-400ad4a1c428"); // dataserver

View File

@ -103,6 +103,7 @@ public:
static const LLUUID DEFAULT_ASSET_ID;
static const F32 DEFAULT_AUTO_ADJUST_PROBE_AMBIANCE;
static F32 sAutoAdjustProbeAmbiance;
typedef PTR_NAMESPACE::shared_ptr<LLSettingsSky> ptr_t;

View File

@ -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)
{

View File

@ -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);

View File

@ -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();
}
}

View File

@ -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

View File

@ -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,313 +43,44 @@
#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();
}
///////////////////////////////////////////////////////////
void LLPacketRing::cleanup ()
{
LLPacketBuffer *packetp;
while (!mReceiveQueue.empty())
for (auto packet : mPacketRing)
{
packetp = mReceiveQueue.front();
delete packetp;
mReceiveQueue.pop();
}
while (!mSendQueue.empty())
{
packetp = mSendQueue.front();
delete packetp;
mSendQueue.pop();
delete packet;
}
mPacketRing.clear();
mNumBufferedPackets = 0;
mNumBufferedBytes = 0;
mHeadIndex = 0;
}
///////////////////////////////////////////////////////////
void LLPacketRing::dropPackets (U32 num_to_drop)
{
mPacketsToDrop += num_to_drop;
}
///////////////////////////////////////////////////////////
void LLPacketRing::setDropPercentage (F32 percent_to_drop)
{
mDropPercentage = percent_to_drop;
}
void LLPacketRing::setUseInThrottle(const bool use_throttle)
{
mUseInThrottle = use_throttle;
}
void LLPacketRing::setUseOutThrottle(const bool use_throttle)
{
mUseOutThrottle = use_throttle;
}
void LLPacketRing::setInBandwidth(const F32 bps)
{
mInThrottle.setRate(bps);
}
void LLPacketRing::setOutBandwidth(const F32 bps)
{
mOutThrottle.setRate(bps);
}
///////////////////////////////////////////////////////////
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())
{
// No packets on the queue, don't give them any.
return 0;
}
S32 packet_size = 0;
packetp = mReceiveQueue.front();
mReceiveQueue.pop();
packet_size = packetp->getSize();
if (packetp->getData() != NULL)
{
memcpy(datap, packetp->getData(), packet_size); /*Flawfinder: ignore*/
}
// 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;
}
///////////////////////////////////////////////////////////
S32 LLPacketRing::receivePacket (S32 socket, char *datap)
{
S32 packet_size = 0;
// If using the throttle, simulate a limited size input buffer.
if (mUseInThrottle)
{
bool done = false;
// push any current net packet (if any) onto delay ring
while (!done)
{
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--;
}
}
// If we faked packet loss, then we don't have a packet
// to use for buffer overflow testing
if (packetp)
{
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;
}
}
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);
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;
}
}
else
{
packet_size = receive_packet(socket, datap);
mLastSender = ::get_sender();
}
mLastReceivingIF = ::get_receiving_interface();
if (packet_size) // did we actually get a packet?
{
if (mDropPercentage && (ll_frand(100.f) < mDropPercentage))
{
mPacketsToDrop++;
}
if (mPacketsToDrop)
{
packet_size = 0;
mPacketsToDrop--;
}
}
}
return packet_size;
bool drop = computeDrop();
return (mNumBufferedPackets > 0) ?
receiveOrDropBufferedPacket(datap, drop) :
receiveOrDropPacket(socket, datap, drop);
}
bool LLPacketRing::sendPacket(int h_socket, char * send_buffer, S32 buf_size, LLHost host)
bool send_packet_helper(int socket, const char * datap, S32 data_size, LLHost host)
{
bool status = true;
if (!mUseOutThrottle)
{
return sendPacketImpl(h_socket, send_buffer, buf_size, host );
}
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
S32 packet_size = 0;
if (!mSendQueue.empty())
{
// Send a packet off of the queue
LLPacketBuffer *packetp = mSendQueue.front();
mSendQueue.pop();
mOutBufferLength -= packetp->getSize();
packet_size = packetp->getSize();
status = sendPacketImpl(h_socket, packetp->getData(), packet_size, packetp->getHost());
delete packetp;
// Update the throttle
mOutThrottle.throttleOverflow(packet_size * 8.f);
}
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;
}
}
// 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
{
static LLTimer queue_timer;
if ((mOutBufferLength > 4192) && queue_timer.getElapsedTimeF32() > 1.f)
{
// Add it to the queue
LL_INFOS() << "Outbound packet queue " << mOutBufferLength << " bytes" << LL_ENDL;
queue_timer.reset();
}
packetp = new LLPacketBuffer(host, send_buffer, buf_size);
mOutBufferLength += packetp->getSize();
mSendQueue.push(packetp);
}
}
return status;
}
bool LLPacketRing::sendPacketImpl(int h_socket, const char * send_buffer, S32 buf_size, LLHost host)
{
if (!LLProxy::isSOCKSProxyEnabled())
{
return send_packet(h_socket, send_buffer, buf_size, host.getAddress(), host.getPort());
return send_packet(socket, datap, data_size, host.getAddress(), host.getPort());
}
char headered_send_buffer[NET_BUFFER_SIZE + SOCKS_HEADER_SIZE];
@ -361,11 +92,268 @@ bool LLPacketRing::sendPacketImpl(int h_socket, const char * send_buffer, S32 bu
socks_header->atype = ADDRESS_IPV4;
socks_header->frag = 0;
memcpy(headered_send_buffer + SOCKS_HEADER_SIZE, send_buffer, buf_size);
memcpy(headered_send_buffer + SOCKS_HEADER_SIZE, datap, data_size);
return send_packet( h_socket,
return send_packet( socket,
headered_send_buffer,
buf_size + SOCKS_HEADER_SIZE,
data_size + SOCKS_HEADER_SIZE,
LLProxy::getInstance()->getUDPProxy().getAddress(),
LLProxy::getInstance()->getUDPProxy().getPort());
}
bool LLPacketRing::sendPacket(int socket, const char * datap, S32 data_size, LLHost host)
{
mActualBytesOut += data_size;
return send_packet_helper(socket, datap, data_size, host);
}
void LLPacketRing::dropPackets (U32 num_to_drop)
{
mPacketsToDrop += num_to_drop;
}
void LLPacketRing::setDropPercentage (F32 percent_to_drop)
{
mDropPercentage = percent_to_drop;
}
bool LLPacketRing::computeDrop()
{
bool drop= (mDropPercentage > 0.0f && (ll_frand(100.f) < mDropPercentage));
if (drop)
{
++mPacketsToDrop;
}
if (mPacketsToDrop > 0)
{
--mPacketsToDrop;
drop = true;
}
return drop;
}
S32 LLPacketRing::receiveOrDropPacket(S32 socket, char *datap, bool drop)
{
S32 packet_size = 0;
// pull straight from socket
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)
{
if (drop)
{
packet_size = 0;
}
else
{
// *FIX We are assuming ATYP is 0x01 (IPv4), not 0x03 (hostname) or 0x04 (IPv6)
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));
mLastReceivingIF = ::get_receiving_interface();
}
}
else
{
packet_size = 0;
}
}
else
{
packet_size = receive_packet(socket, datap);
if (packet_size > 0)
{
mActualBytesIn += packet_size;
if (drop)
{
packet_size = 0;
}
else
{
mLastSender = ::get_sender();
mLastReceivingIF = ::get_receiving_interface();
}
}
}
return packet_size;
}
S32 LLPacketRing::receiveOrDropBufferedPacket(char *datap, bool drop)
{
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)
{
assert(mNumBufferedBytes == 0);
}
if (!drop)
{
assert(packet_size > 0);
memcpy(datap, packet->getData(), packet_size);
}
else
{
packet_size = 0;
}
return packet_size;
}
S32 LLPacketRing::bufferInboundPacket(S32 socket)
{
if (mNumBufferedPackets == mPacketRing.size() && mNumBufferedPackets < MAX_BUFFER_RING_SIZE)
{
expandRing();
}
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)
{
// *FIX We are assuming ATYP is 0x01 (IPv4), not 0x03 (hostname) or 0x04 (IPv6)
proxywrap_t * header = static_cast<proxywrap_t*>(static_cast<void*>(buffer));
LLHost sender;
sender.setAddress(header->addr);
sender.setPort(ntohs(header->port));
packet_size -= SOCKS_HEADER_SIZE; // The unwrapped packet size
packet->init(buffer + SOCKS_HEADER_SIZE, packet_size, sender);
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
{
packet_size = 0;
}
}
}
else
{
packet->init(socket);
packet_size = packet->getSize();
if (packet_size > 0)
{
mActualBytesIn += packet_size;
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;
}
}
}
return packet_size;
}
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);
}
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)
{
// mPacketRing is already maxed out
return false;
}
// 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];
}
// 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);
}
// swap the rings and reset mHeadIndex
mPacketRing.swap(new_ring);
mHeadIndex = mNumBufferedPackets;
return true;
}
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;
}

View File

@ -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;}
S32 getNumBufferedPackets() const { return (S32)(mNumBufferedPackets); }
S32 getNumBufferedBytes() const { return mNumBufferedBytes; }
S32 getNumDroppedPackets() const { return mNumDroppedPacketsTotal + mNumDroppedPackets; }
void dumpPacketRingStats();
protected:
bool mUseInThrottle;
bool mUseOutThrottle;
// returns 'true' if we should intentionally drop a packet
bool computeDrop();
// For simulating a lower-bandwidth connection - BPS
LLThrottle mInThrottle;
LLThrottle mOutThrottle;
// 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);
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 packet buffered
S32 bufferInboundPacket(S32 socket);
F32 mDropPercentage; // % of packets to drop
U32 mPacketsToDrop; // drop next n packets
// returns 'true' if ring was expanded
bool expandRing();
std::queue<LLPacketBuffer *> mReceiveQueue;
std::queue<LLPacketBuffer *> mSendQueue;
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

View File

@ -465,7 +465,7 @@ void LLProxy::applyProxySettings(CURL* handle)
/**
* @brief Send one TCP packet and receive one in return.
*
* This operation is done synchronously with a 1000ms timeout. Therefore, it should not be used when a blocking
* This operation is done synchronously with a 100ms timeout. Therefore, it should not be used when a blocking
* operation would impact the operation of the viewer.
*
* @param handle_ptr Pointer to a connected LLSocket of type STREAM_TCP.
@ -482,7 +482,7 @@ static apr_status_t tcp_blocking_handshake(LLSocket::ptr_t handle, char * dataou
apr_size_t expected_len = outlen;
handle->setBlocking(1000);
handle->setBlocking(100000); // 100ms, 100000us. Should be sufficient for localhost, nearby network
rv = apr_socket_send(apr_socket, dataout, &outlen);
if (APR_SUCCESS != rv)

View File

@ -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

View File

@ -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)
{

View File

@ -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)

View File

@ -127,7 +127,7 @@ void LLCubeMapArray::allocate(U32 resolution, U32 components, U32 count, bool us
bind(0);
free_cur_tex_image();
U32 format = components == 4 ? GL_RGBA16F : GL_RGB16F;
U32 format = components == 4 ? GL_RGBA16F : GL_R11F_G11F_B10F;
if (!hdr)
{
format = components == 4 ? GL_RGBA8 : GL_RGB8;

View File

@ -107,7 +107,7 @@ bool LLFontBitmapCache::nextOpenPos(S32 width, S32& pos_x, S32& pos_y, EFontGlyp
mBitmapHeight = image_height;
S32 num_components = getNumComponents(bitmap_type);
mImageRawVec[bitmap_idx].push_back(new LLImageRaw(mBitmapWidth, mBitmapHeight, num_components));
mImageRawVec[bitmap_idx].emplace_back(new LLImageRaw(mBitmapWidth, mBitmapHeight, num_components));
bitmap_num = static_cast<U32>(mImageRawVec[bitmap_idx].size()) - 1;
LLImageRaw* image_raw = getImageRaw(bitmap_type, bitmap_num);
@ -117,7 +117,7 @@ bool LLFontBitmapCache::nextOpenPos(S32 width, S32& pos_x, S32& pos_y, EFontGlyp
}
// Make corresponding GL image.
mImageGLVec[bitmap_idx].push_back(new LLImageGL(image_raw, false, false));
mImageGLVec[bitmap_idx].emplace_back(new LLImageGL(image_raw, false, false));
LLImageGL* image_gl = getImageGL(bitmap_type, bitmap_num);
// Start at beginning of the new image.
@ -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

View File

@ -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)];
};

View File

@ -143,7 +143,6 @@ LLFontFreetype::LLFontFreetype()
mIsFallback(false),
mFTFace(nullptr),
mRenderGlyphCount(0),
mAddGlyphCount(0),
mStyle(0),
mPointSize(0)
{
@ -502,7 +501,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;
@ -643,7 +641,7 @@ void LLFontFreetype::renderGlyph(EFontGlyphType bitmap_type, U32 glyph_index, ll
if (error == FT_Err_Out_Of_Memory)
{
LLError::LLUserWarningMsg::showOutOfMemory();
LL_ERRS() << "Out of memory loading glyph for character " << U32(wch) << LL_ENDL;
LL_ERRS() << "Out of memory loading glyph for character " << llformat("U+%xu", U32(wch)) << LL_ENDL;
}
std::string message = llformat(
@ -655,11 +653,11 @@ void LLFontFreetype::renderGlyph(EFontGlyphType bitmap_type, U32 glyph_index, ll
|| FT_Err_Invalid_Composite == error
|| (FT_Err_Ok != error && LLStringOps::isEmoji(wch)))
{
glyph_index = FT_Get_Char_Index(mFTFace, '?');
// if '?' is not present, potentially can use last index, that's supposed to be null glyph
if (glyph_index > 0)
// value~0 always corresponds to the 'missing glyph'
error = FT_Load_Glyph(mFTFace, 0, FT_LOAD_FORCE_AUTOHINT);
if (FT_Err_Ok != error)
{
error = FT_Load_Glyph(mFTFace, glyph_index, load_flags ^ FT_LOAD_COLOR);
LL_ERRS() << "Loading fallback for char '" << (U32)wch << "', glyph " << glyph_index << " failed with error : " << (S32)error << LL_ENDL;
}
}
llassert_always_msg(FT_Err_Ok == error, message.c_str());

View File

@ -190,7 +190,6 @@ private:
mutable LLFontBitmapCache* mFontBitmapCachep;
mutable S32 mRenderGlyphCount;
mutable S32 mAddGlyphCount;
};
#endif // LL_FONTFREETYPE_H

View File

@ -110,6 +110,12 @@ S32 LLFontGL::getNumFaces(const std::string& filename)
return mFontFreetype->getNumFaces(filename);
}
S32 LLFontGL::getCacheGeneration() const
{
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,
ShadowType shadow, S32 max_chars, F32* right_x, bool use_ellipses, bool use_color) const
{
@ -250,6 +256,10 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons
const LLFontBitmapCache* font_bitmap_cache = mFontFreetype->getFontBitmapCache();
// This looks wrong, value is dynamic.
// LLFontBitmapCache::nextOpenPos can alter these values when
// new characters get added to cache, which affects whole string.
// Todo: Perhaps value should update after symbols were added?
F32 inv_width = 1.f / font_bitmap_cache->getBitmapWidth();
F32 inv_height = 1.f / font_bitmap_cache->getBitmapHeight();
@ -271,12 +281,14 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons
const LLFontGlyphInfo* next_glyph = NULL;
// string can have more than one glyph per char (ex: bold or shadow),
// make sure that GLYPH_BATCH_SIZE won't end up with half a symbol.
// See drawGlyph.
// Ex: with shadows it's 6 glyps per char. 30 fits exactly 5 chars.
static constexpr S32 GLYPH_BATCH_SIZE = 30;
// string can have more than one glyph per char, make sure last one can fit
static constexpr S32 BUFFER_SIZE = GLYPH_BATCH_SIZE * 2;
static thread_local LLVector4a vertices[BUFFER_SIZE * 6];
static thread_local LLVector2 uvs[BUFFER_SIZE * 6];
static thread_local LLColor4U colors[BUFFER_SIZE * 6];
static thread_local LLVector4a vertices[GLYPH_BATCH_SIZE * 6];
static thread_local LLVector2 uvs[GLYPH_BATCH_SIZE * 6];
static thread_local LLColor4U colors[GLYPH_BATCH_SIZE * 6];
LLColor4U text_color(color);
// Preserve the transparency to render fading emojis in fading text (e.g.
@ -321,8 +333,9 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons
LLImageGL* font_image = font_bitmap_cache->getImageGL(bitmap_entry.first, bitmap_entry.second);
gGL.getTexUnit(0)->bind(font_image);
// For multi-byte characters just draw each time character changes
// Might be overkill and might be better to detect multybyte
// For some reason it's not enough to compare by bitmap_entry.
// Issue hits emojis, japenese and chinese glyphs, only on first run.
// Todo: figure it out, there might be a bug with raw image data.
last_char = wch;
}

View File

@ -90,6 +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 getCacheGeneration() const;
S32 render(const LLWString &text, S32 begin_offset,
const LLRect& rect,

View File

@ -147,7 +147,8 @@ S32 LLFontVertexBuffer::render(
|| mLastVertDPI != LLFontGL::sVertDPI
|| mLastHorizDPI != LLFontGL::sHorizDPI
|| mLastOrigin != LLFontGL::sCurOrigin
|| mLastResGeneration != LLFontGL::sResolutionGeneration)
|| mLastResGeneration != LLFontGL::sResolutionGeneration
|| 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);
@ -179,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,

View File

@ -120,6 +120,10 @@ private:
S32 mLastResGeneration = 0;
LLCoordGL mLastOrigin;
// 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 mLastFontCacheGen = 0;
static bool sEnableBufferCollection;
};

View File

@ -2452,12 +2452,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];

View File

@ -59,6 +59,7 @@ public:
bool attachNothing = false;
bool hasHeroProbes = false;
bool isPBRTerrain = false;
bool hasTonemap = false;
};
// ============= Structure for caching shader uniforms ===============

View File

@ -330,6 +330,7 @@ S32 LLImageGL::dataFormatBits(S32 dataformat)
case GL_RGB: return 24;
case GL_SRGB: return 24;
case GL_RGB8: return 24;
case GL_R11F_G11F_B10F: return 32;
case GL_RGBA: return 32;
case GL_RGBA8: return 32;
case GL_RGB10_A2: return 32;

View File

@ -291,6 +291,14 @@ bool LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader)
}
}
if (features->hasTonemap)
{
if (!shader->attachFragmentObject("deferred/tonemapUtilF.glsl"))
{
return false;
}
}
// NOTE order of shader object attaching is VERY IMPORTANT!!!
if (features->hasAtmospherics)
{

View File

@ -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()

View File

@ -3394,7 +3394,7 @@ bool LLScrollListCtrl::highlightMatchingItems(const std::string& filter_str)
bool res = false;
setHighlightedColor(LLUIColorTable::instance().getColor("SearchableControlHighlightColor", LLColor4::red));
setHighlightedColor(LLUIColorTable::instance().getColor("SearchableControlHighlightColor", LLColor4::red4));
std::string filter_str_lc(filter_str);
LLStringUtil::toLower(filter_str_lc);

View File

@ -45,7 +45,7 @@ namespace ll
const LLColor4& getHighlightColor( ) const
{
static LLUIColor highlight_color = LLUIColorTable::instance().getColor("SearchableControlHighlightColor", LLColor4::red);
static LLUIColor highlight_color = LLUIColorTable::instance().getColor("SearchableControlHighlightColor", LLColor4::red4);
return highlight_color.get();
}

View File

@ -76,6 +76,12 @@ protected:
LLUIString mText;
callback_t mClickedCallback;
bool mShowCursorHand;
protected:
virtual std::string _getSearchText() const
{
return LLTextBase::_getSearchText() + mText.getString();
}
};
// Build time optimization, generate once in .cpp file

View File

@ -306,6 +306,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);
}

View File

@ -108,12 +108,15 @@ LLXmlTreeNode::LLXmlTreeNode( const std::string& name, LLXmlTreeNode* parent, LL
LLXmlTreeNode::~LLXmlTreeNode()
{
attribute_map_t::iterator iter;
for (iter=mAttributes.begin(); iter != mAttributes.end(); iter++)
delete iter->second;
for(LLXmlTreeNode* node : mChildren)
for (auto& attr : mAttributes)
{
delete node;
delete attr.second;
}
mAttributes.clear();
for (auto& child : mChildren)
{
delete child;
}
mChildren.clear();
}

View File

@ -1 +1 @@
7.1.12
7.1.13

View File

@ -7794,6 +7794,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>
@ -9065,6 +9076,17 @@
<key>Value</key>
<integer>1</integer>
</map>
<key>RenderReflectionProbeCount</key>
<map>
<key>Comment</key>
<string>Number of probes to render. Maximum of 256. Clamps to the nearest power of 2.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>U32</string>
<key>Value</key>
<integer>256</integer>
</map>
<key>RenderReflectionProbeResolution</key>
<map>
<key>Comment</key>
@ -11618,7 +11640,7 @@
<key>Type</key>
<string>F32</string>
<key>Value</key>
<real>0.04</real>
<real>0.0095</real>
</map>
<key>TextureScaleMaxAreaFactor</key>
<map>

View File

@ -80,6 +80,18 @@ 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(1), isinf(color));
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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -28,138 +28,13 @@
out vec4 frag_color;
uniform sampler2D diffuseRect;
uniform sampler2D exposureMap;
uniform vec2 screen_res;
in vec2 vary_fragcoord;
vec3 linear_to_srgb(vec3 cl);
vec3 toneMap(vec3 color);
//===============================================================
// tone mapping taken from Khronos sample implementation
//===============================================================
// sRGB => XYZ => D65_2_D60 => AP1 => RRT_SAT
const mat3 ACESInputMat = mat3
(
0.59719, 0.07600, 0.02840,
0.35458, 0.90834, 0.13383,
0.04823, 0.01566, 0.83777
);
// ODT_SAT => XYZ => D60_2_D65 => sRGB
const mat3 ACESOutputMat = mat3
(
1.60475, -0.10208, -0.00327,
-0.53108, 1.10813, -0.07276,
-0.07367, -0.00605, 1.07602
);
// ACES tone map (faster approximation)
// see: https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/
vec3 toneMapACES_Narkowicz(vec3 color)
{
const float A = 2.51;
const float B = 0.03;
const float C = 2.43;
const float D = 0.59;
const float E = 0.14;
return clamp((color * (A * color + B)) / (color * (C * color + D) + E), 0.0, 1.0);
}
// ACES filmic tone map approximation
// see https://github.com/TheRealMJP/BakingLab/blob/master/BakingLab/ACES.hlsl
vec3 RRTAndODTFit(vec3 color)
{
vec3 a = color * (color + 0.0245786) - 0.000090537;
vec3 b = color * (0.983729 * color + 0.4329510) + 0.238081;
return a / b;
}
// tone mapping
vec3 toneMapACES_Hill(vec3 color)
{
color = ACESInputMat * color;
// Apply RRT and ODT
color = RRTAndODTFit(color);
color = ACESOutputMat * color;
// Clamp to [0, 1]
color = clamp(color, 0.0, 1.0);
return color;
}
// Khronos Neutral tonemapping
// https://github.com/KhronosGroup/ToneMapping/tree/main
// Input color is non-negative and resides in the Linear Rec. 709 color space.
// Output color is also Linear Rec. 709, but in the [0, 1] range.
vec3 PBRNeutralToneMapping( vec3 color )
{
const float startCompression = 0.8 - 0.04;
const float desaturation = 0.15;
float x = min(color.r, min(color.g, color.b));
float offset = x < 0.08 ? x - 6.25 * x * x : 0.04;
color -= offset;
float peak = max(color.r, max(color.g, color.b));
if (peak < startCompression) return color;
const float d = 1. - startCompression;
float newPeak = 1. - d * d / (peak + d - startCompression);
color *= newPeak / peak;
float g = 1. - 1. / (desaturation * (peak - newPeak) + 1.);
return mix(color, newPeak * vec3(1, 1, 1), g);
}
uniform float exposure;
uniform float tonemap_mix;
uniform int tonemap_type;
vec3 toneMap(vec3 color)
{
#ifndef NO_POST
float exp_scale = texture(exposureMap, vec2(0.5,0.5)).r;
color *= exposure * exp_scale;
vec3 clamped_color = clamp(color.rgb, vec3(0.0), vec3(1.0));
switch(tonemap_type)
{
case 0:
color = PBRNeutralToneMapping(color);
break;
case 1:
color = toneMapACES_Hill(color);
break;
}
// mix tonemapped and linear here to provide adjustment
color = mix(clamped_color, color, tonemap_mix);
#endif
return color;
}
//===============================================================
void debugExposure(inout vec3 color)
{
float exp_scale = texture(exposureMap, vec2(0.5,0.5)).r;
exp_scale *= 0.5;
if (abs(vary_fragcoord.y-exp_scale) < 0.01 && vary_fragcoord.x < 0.1)
{
color = vec3(1,0,0);
}
}
vec3 clampHDRRange(vec3 color);
void main()
{
@ -172,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));
}

View File

@ -0,0 +1,180 @@
/**
* @file postDeferredTonemap.glsl
*
* $LicenseInfo:firstyear=2024&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2024, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
/*[EXTRA_CODE_HERE]*/
uniform sampler2D exposureMap;
uniform vec2 screen_res;
in vec2 vary_fragcoord;
//===============================================================
// tone mapping taken from Khronos sample implementation
//===============================================================
// sRGB => XYZ => D65_2_D60 => AP1 => RRT_SAT
const mat3 ACESInputMat = mat3
(
0.59719, 0.07600, 0.02840,
0.35458, 0.90834, 0.13383,
0.04823, 0.01566, 0.83777
);
// ODT_SAT => XYZ => D60_2_D65 => sRGB
const mat3 ACESOutputMat = mat3
(
1.60475, -0.10208, -0.00327,
-0.53108, 1.10813, -0.07276,
-0.07367, -0.00605, 1.07602
);
// ACES tone map (faster approximation)
// see: https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/
vec3 toneMapACES_Narkowicz(vec3 color)
{
const float A = 2.51;
const float B = 0.03;
const float C = 2.43;
const float D = 0.59;
const float E = 0.14;
return clamp((color * (A * color + B)) / (color * (C * color + D) + E), 0.0, 1.0);
}
// ACES filmic tone map approximation
// see https://github.com/TheRealMJP/BakingLab/blob/master/BakingLab/ACES.hlsl
vec3 RRTAndODTFit(vec3 color)
{
vec3 a = color * (color + 0.0245786) - 0.000090537;
vec3 b = color * (0.983729 * color + 0.4329510) + 0.238081;
return a / b;
}
// tone mapping
vec3 toneMapACES_Hill(vec3 color)
{
color = ACESInputMat * color;
// Apply RRT and ODT
color = RRTAndODTFit(color);
color = ACESOutputMat * color;
// Clamp to [0, 1]
color = clamp(color, 0.0, 1.0);
return color;
}
// Khronos Neutral tonemapping
// https://github.com/KhronosGroup/ToneMapping/tree/main
// Input color is non-negative and resides in the Linear Rec. 709 color space.
// Output color is also Linear Rec. 709, but in the [0, 1] range.
vec3 PBRNeutralToneMapping( vec3 color )
{
const float startCompression = 0.8 - 0.04;
const float desaturation = 0.15;
float x = min(color.r, min(color.g, color.b));
float offset = x < 0.08 ? x - 6.25 * x * x : 0.04;
color -= offset;
float peak = max(color.r, max(color.g, color.b));
if (peak < startCompression) return color;
const float d = 1. - startCompression;
float newPeak = 1. - d * d / (peak + d - startCompression);
color *= newPeak / peak;
float g = 1. - 1. / (desaturation * (peak - newPeak) + 1.);
return mix(color, newPeak * vec3(1, 1, 1), g);
}
uniform float exposure;
uniform float tonemap_mix;
uniform int tonemap_type;
vec3 toneMap(vec3 color)
{
#ifndef NO_POST
float exp_scale = texture(exposureMap, vec2(0.5,0.5)).r;
color *= exposure * exp_scale;
vec3 clamped_color = clamp(color.rgb, vec3(0.0), vec3(1.0));
switch(tonemap_type)
{
case 0:
color = PBRNeutralToneMapping(color);
break;
case 1:
color = toneMapACES_Hill(color);
break;
}
// mix tonemapped and linear here to provide adjustment
color = mix(clamped_color, color, tonemap_mix);
#endif
return color;
}
vec3 toneMapNoExposure(vec3 color)
{
#ifndef NO_POST
vec3 clamped_color = clamp(color.rgb, vec3(0.0), vec3(1.0));
switch(tonemap_type)
{
case 0:
color = PBRNeutralToneMapping(color);
break;
case 1:
color = toneMapACES_Hill(color);
break;
}
// mix tonemapped and linear here to provide adjustment
color = mix(clamped_color, color, tonemap_mix);
#endif
return color;
}
//===============================================================
void debugExposure(inout vec3 color)
{
float exp_scale = texture(exposureMap, vec2(0.5,0.5)).r;
exp_scale *= 0.5;
if (abs(vary_fragcoord.y-exp_scale) < 0.01 && vary_fragcoord.x < 0.1)
{
color = vec3(1,0,0);
}
}

View File

@ -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;
}

View File

@ -90,21 +90,15 @@ uniform sampler2D depthMap;
uniform sampler2D exclusionTex;
uniform float sunAngle;
uniform float sunAngle2;
uniform int classic_mode;
uniform vec3 lightDir;
uniform vec3 specular;
uniform float lightExp;
uniform float blurMultiplier;
uniform float refScale;
uniform float kd;
uniform vec2 screenRes;
uniform vec3 normScale;
uniform float fresnelScale;
uniform float fresnelOffset;
uniform float blurMultiplier;
uniform vec4 waterFogColor;
uniform vec3 waterFogColorLinear;
//bigWave is (refCoord.w, view.w);
in vec4 refCoord;
@ -126,6 +120,7 @@ vec3 linear_to_srgb(vec3 col);
vec3 atmosLighting(vec3 light);
vec3 scaleSoftClip(vec3 light);
vec3 toneMapNoExposure(vec3 color);
vec3 vN, vT, vB;
@ -171,18 +166,18 @@ void calculateFresnelFactors(out vec3 df3, out vec2 df2, vec3 viewVec, vec3 wave
// We calculate the fresnel here.
// We do this by getting the dot product for each sets of waves, and applying scale and offset.
df3 = vec3(
df3 = max(vec3(0), vec3(
dot(viewVec, wave1),
dot(viewVec, (wave2 + wave3) * 0.5),
dot(viewVec, wave3)
) * fresnelScale + fresnelOffset;
) * fresnelScale + fresnelOffset);
df3 *= df3;
df2 = vec2(
df2 = max(vec2(0), vec2(
df3.x + df3.y + df3.z,
dot(viewVec, wavef) * fresnelScale + fresnelOffset
);
));
}
void main()
@ -194,6 +189,7 @@ void main()
vB = cross(vN, vT);
vec3 pos = vary_position.xyz;
float linear_depth = 1 / -pos.z;
float dist = length(pos.xyz);
@ -216,6 +212,12 @@ void main()
vec3 df3 = vec3(0);
vec2 df2 = vec2(0);
vec3 sunlit;
vec3 amblit;
vec3 additive;
vec3 atten;
calcAtmosphericVarsLinear(pos.xyz, wavef, vary_light_dir, sunlit, amblit, additive, atten);
calculateFresnelFactors(df3, df2, normalize(view.xyz), wave1, wave2, wave3, wavef);
vec3 waver = wavef*3;
@ -230,7 +232,7 @@ void main()
vec3 norm = transform_normal(normalize(wavef));
vdu = clamp(vdu, 0, 1);
wavef.z *= max(vdu*vdu*vdu, 0.1);
//wavef.z *= max(vdu*vdu*vdu, 0.1);
wavef = normalize(wavef);
@ -245,11 +247,6 @@ void main()
distort2 = clamp(distort2, vec2(0), vec2(0.999));
vec3 sunlit;
vec3 amblit;
vec3 additive;
vec3 atten;
float shadow = 1.0f;
float water_mask = texture(exclusionTex, distort).r;
@ -258,17 +255,20 @@ void main()
shadow = sampleDirectionalShadow(pos.xyz, norm.xyz, distort);
#endif
calcAtmosphericVarsLinear(pos.xyz, wavef, vary_light_dir, sunlit, amblit, additive, atten);
vec3 sunlit_linear = srgb_to_linear(sunlit);
float fade = 0;
vec3 sunlit_linear = sunlit;
float fade = 1;
#ifdef TRANSPARENT_WATER
float depth = texture(depthMap, distort).r;
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,min(1, (pos.z - refPos.z) / 10)) * water_mask;
#ifdef SHORELINE_FADE
fade = max(0,min(1, (pos.z - refPos.z) / 10))
#else
fade = 1 * water_mask;
#endif
distort2 = mix(distort, distort2, min(1, fade * 10));
depth = texture(depthMap, distort2).r;
@ -289,8 +289,8 @@ void main()
#endif
float metallic = 1.0;
float perceptualRoughness = 0.1;
float gloss = 0.95;
float perceptualRoughness = blurMultiplier;
float gloss = 1 - perceptualRoughness;
vec3 irradiance = vec3(0);
vec3 radiance = vec3(0);
@ -300,7 +300,7 @@ void main()
#ifdef WATER_MINIMAL
sampleReflectionProbesWater(irradiance, radiance, distort2, pos.xyz, wave_ibl.xyz, gloss, amblit);
#elif WATER_MINIMAL_PLUS
sampleReflectionProbes(irradiance, radiance, distort2, pos.xyz, wave_ibl.xyz, 1, false, amblit);
sampleReflectionProbes(irradiance, radiance, distort2, pos.xyz, wave_ibl.xyz, gloss, false, amblit);
#endif
vec3 diffuseColor = vec3(0);
@ -322,21 +322,27 @@ 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);
color = mix(fb.rgb, radiance * df2.y, df2.x * 0.99999) + punctual.rgb;
color = mix(fb.rgb, radiance, min(1, df2.x)) + punctual.rgb;
float water_haze_scale = 4;
if (classic_mode > 0)
water_haze_scale = 1;
// This looks super janky, but we do this to restore water haze in the distance.
// These values were finagled in to try and bring back some of the distant brightening on legacy water. Also works reasonably well on PBR skies such as PBR midday.
color += color * min(vec3(4),pow(1 - atten, vec3(1.35)) * 16 * fade);
// color = mix(color, additive * water_haze_scale, (1 - atten));
// We shorten the fade here at the shoreline so it doesn't appear too soft from a distance.
fade *= 60;
fade = min(1, fade);
color = mix(fb.rgb, color, fade);
float spec = min(max(max(punctual.r, punctual.g), punctual.b), 0.05);
float spec = min(max(max(punctual.r, punctual.g), punctual.b), 0);
frag_color = min(vec4(1),max(vec4(color.rgb, spec * water_mask), vec4(0)));
}

View File

@ -86,6 +86,7 @@ RenderTonemapType 1 1
RenderTonemapMix 1 1
RenderDisableVintageMode 1 1
RenderMaxTextureResolution 1 2048
RenderReflectionProbeCount 1 256
//
// Low Graphics Settings
@ -128,6 +129,7 @@ RenderTonemapType 1 1
RenderTonemapMix 1 0.7
RenderDisableVintageMode 1 0
RenderMaxTextureResolution 1 512
RenderReflectionProbeCount 1 1
//
// Medium Low Graphics Settings
@ -170,6 +172,7 @@ RenderTonemapType 1 1
RenderTonemapMix 1 0.7
RenderDisableVintageMode 1 0
RenderMaxTextureResolution 1 1024
RenderReflectionProbeCount 1 16
//
// Medium Graphics Settings (standard)
@ -211,6 +214,7 @@ RenderExposure 1 1
RenderTonemapType 1 1
RenderTonemapMix 1 0.7
RenderMaxTextureResolution 1 2048
RenderReflectionProbeCount 1 32
//
// Medium High Graphics Settings
@ -252,6 +256,7 @@ RenderExposure 1 1
RenderTonemapType 1 1
RenderTonemapMix 1 0.7
RenderMaxTextureResolution 1 2048
RenderReflectionProbeCount 1 64
//
// High Graphics Settings (SSAO + sun shadows)
@ -293,6 +298,7 @@ RenderExposure 1 1
RenderTonemapType 1 1
RenderTonemapMix 1 0.7
RenderMaxTextureResolution 1 2048
RenderReflectionProbeCount 1 128
//
// High Ultra Graphics Settings (deferred + SSAO + all shadows)
@ -334,6 +340,7 @@ RenderExposure 1 1
RenderTonemapType 1 1
RenderTonemapMix 1 0.7
RenderMaxTextureResolution 1 2048
RenderReflectionProbeCount 1 256
//
// Ultra graphics (REALLY PURTY!)
@ -375,6 +382,7 @@ RenderExposure 1 1
RenderTonemapType 1 1
RenderTonemapMix 1 0.7
RenderMaxTextureResolution 1 2048
RenderReflectionProbeCount 1 256
//
// Class Unknown Hardware (unknown)
@ -408,6 +416,7 @@ RenderReflectionProbeDetail 0 -1
RenderMirrors 0 0
RenderDisableVintageMode 1 0
RenderMaxTextureResolution 1 2048
RenderReflectionProbeCount 0 0
list Intel
RenderAnisotropic 1 0
@ -429,6 +438,7 @@ RenderMirrors 0 0
RenderGLMultiThreadedTextures 0 0
RenderGLMultiThreadedMedia 0 0
RenderDisableVintageMode 1 0
RenderReflectionProbeCount 0 0
list TexUnit16orLess
RenderTerrainPBRDetail 1 -1

View File

@ -86,6 +86,7 @@ RenderTonemapMix 1 1
RenderDisableVintageMode 1 1
RenderDownScaleMethod 1 0
RenderMaxTextureResolution 1 2048
RenderReflectionProbeCount 1 256
//
// Low Graphics Settings
@ -128,6 +129,7 @@ RenderTonemapType 1 1
RenderTonemapMix 1 0.7
RenderDisableVintageMode 1 0
RenderMaxTextureResolution 1 512
RenderReflectionProbeCount 1 1
//
// Medium Low Graphics Settings
@ -170,6 +172,7 @@ RenderTonemapType 1 1
RenderTonemapMix 1 0.7
RenderDisableVintageMode 1 0
RenderMaxTextureResolution 1 1024
RenderReflectionProbeCount 1 16
//
// Medium Graphics Settings (standard)
@ -211,6 +214,7 @@ RenderExposure 1 1
RenderTonemapType 1 1
RenderTonemapMix 1 0.7
RenderMaxTextureResolution 1 2048
RenderReflectionProbeCount 1 32
//
// Medium High Graphics Settings
@ -252,6 +256,7 @@ RenderExposure 1 1
RenderTonemapType 1 1
RenderTonemapMix 1 0.7
RenderMaxTextureResolution 1 2048
RenderReflectionProbeCount 1 64
//
// High Graphics Settings (SSAO + sun shadows)
@ -293,6 +298,7 @@ RenderExposure 1 1
RenderTonemapType 1 1
RenderTonemapMix 1 0.7
RenderMaxTextureResolution 1 2048
RenderReflectionProbeCount 1 128
//
// High Ultra Graphics Settings (SSAO + all shadows)
@ -334,6 +340,7 @@ RenderExposure 1 1
RenderTonemapType 1 1
RenderTonemapMix 1 0.7
RenderMaxTextureResolution 1 2048
RenderReflectionProbeCount 1 256
//
// Ultra graphics (REALLY PURTY!)
@ -375,6 +382,7 @@ RenderExposure 1 1
RenderTonemapType 1 1
RenderTonemapMix 1 0.7
RenderMaxTextureResolution 1 2048
RenderReflectionProbeCount 1 256
//
// Class Unknown Hardware (unknown)
@ -407,6 +415,7 @@ RenderShadowDetail 0 0
RenderMirrors 0 0
RenderDisableVintageMode 1 0
RenderMaxTextureResolution 1 2048
RenderReflectionProbeCount 0 0
list TexUnit8orLess
RenderDeferredSSAO 0 0
@ -447,6 +456,7 @@ RenderReflectionProbeDetail 0 0
RenderReflectionsEnabled 0 0
RenderMirrors 0 0
RenderDisableVintageMode 1 0
RenderReflectionProbeCount 0 0
list VaryingVectors16orLess
RenderTerrainPBRPlanarSampleCount 1 1

View File

@ -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;

View File

@ -56,6 +56,7 @@
#include "llgroupmgr.h"
#include "llhudmanager.h"
#include "lljoystickbutton.h"
#include "lllandmarkactions.h"
#include "llmorphview.h"
#include "llmoveview.h"
#include "llnavigationbar.h" // to show/hide navigation bar when changing mouse look state
@ -2501,7 +2502,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
@ -4317,8 +4321,22 @@ void LLAgent::teleportViaLandmark(const LLUUID& landmark_asset_id)
void LLAgent::doTeleportViaLandmark(const LLUUID& landmark_asset_id)
{
LLViewerRegion *regionp = getRegion();
if(regionp && teleportCore())
LLViewerRegion* regionp = getRegion();
if (!regionp)
{
LL_WARNS("Teleport") << "called when agent region is null" << LL_ENDL;
return;
}
bool is_local(false);
if (LLLandmark* landmark = gLandmarkList.getAsset(landmark_asset_id, NULL))
{
LLVector3d pos_global;
landmark->getGlobalPos(pos_global);
is_local = (regionp->getHandle() == to_region_handle_global((F32)pos_global.mdV[VX], (F32)pos_global.mdV[VY]));
}
if(regionp && teleportCore(is_local))
{
LL_INFOS("Teleport") << "Sending TeleportLandmarkRequest. Current region handle " << regionp->getHandle()
<< " region id " << regionp->getRegionID()

View File

@ -28,6 +28,7 @@
#include "llagentpicksinfo.h"
#include "llagent.h"
#include "llagentbenefits.h"
#include "llavatarpropertiesprocessor.h"
const S32 MAX_AVATAR_PICKS = 10;
@ -85,10 +86,9 @@ private:
LLAgentPicksInfo::LLAgentPicksInfo()
: mAgentPicksObserver(NULL)
, mMaxNumberOfPicks(MAX_AVATAR_PICKS)
// Disable Pick creation until we get number of Picks from server - in case
// avatar has maximum number of Picks.
, mNumberOfPicks(mMaxNumberOfPicks)
, mNumberOfPicks(S32_MAX)
{
}
@ -110,7 +110,13 @@ void LLAgentPicksInfo::requestNumberOfPicks()
mAgentPicksObserver->sendAgentPicksRequest();
}
bool LLAgentPicksInfo::isPickLimitReached()
// static
S32 LLAgentPicksInfo::getMaxNumberOfPicks()
{
return LLAgentBenefitsMgr::current().getPicksLimit();
}
bool LLAgentPicksInfo::isPickLimitReached() const
{
return getNumberOfPicks() >= getMaxNumberOfPicks();
}

View File

@ -52,17 +52,17 @@ public:
/**
* Returns number of Picks.
*/
S32 getNumberOfPicks() { return mNumberOfPicks; }
S32 getNumberOfPicks() const { return mNumberOfPicks; }
/**
* Returns maximum number of Picks.
*/
S32 getMaxNumberOfPicks() { return mMaxNumberOfPicks; }
static S32 getMaxNumberOfPicks();
/**
* Returns true if Agent has maximum allowed number of Picks.
*/
bool isPickLimitReached();
bool isPickLimitReached() const;
/**
* After creating or deleting a Pick we can assume operation on server will be
@ -83,15 +83,9 @@ private:
*/
void setNumberOfPicks(S32 number) { mNumberOfPicks = number; }
/**
* Sets maximum number of Picks.
*/
void setMaxNumberOfPicks(S32 max_picks) { mMaxNumberOfPicks = max_picks; }
private:
LLAgentPicksObserver* mAgentPicksObserver;
S32 mMaxNumberOfPicks;
S32 mNumberOfPicks;
};

View File

@ -1385,8 +1385,6 @@ void AISUpdate::parseCategory(const LLSD& category_map, S32 depth)
&& curr_cat->getVersion() > LLViewerInventoryCategory::VERSION_UNKNOWN
&& version > curr_cat->getVersion())
{
// Potentially should new_cat->setVersion(unknown) here,
// but might be waiting for a callback that would increment
LL_DEBUGS("Inventory") << "Category " << category_id
<< " is stale. Known version: " << curr_cat->getVersion()
<< " server version: " << version << LL_ENDL;

View File

@ -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;

View File

@ -349,10 +349,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;
@ -361,6 +357,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.
@ -374,7 +371,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)
@ -1500,9 +1496,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
@ -2359,6 +2355,14 @@ void LLAppViewer::initLoggingAndGetLastDuration()
{
LL_WARNS("MarkerFile") << duration_log_msg << LL_ENDL;
}
std::string user_data_path_cef_log = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "cef.log");
if (gDirUtilp->fileExists(user_data_path_cef_log))
{
std::string user_data_path_cef_old = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "cef.old");
LLFile::remove(user_data_path_cef_old, ENOENT);
LLFile::rename(user_data_path_cef_log, user_data_path_cef_old);
}
}
}
@ -2986,9 +2990,10 @@ void LLAppViewer::initStrings()
std::string strings_path_full = gDirUtilp->findSkinnedFilenameBaseLang(LLDir::XUI, strings_file);
if (strings_path_full.empty() || !LLFile::isfile(strings_path_full))
{
std::string crash_reason;
if (strings_path_full.empty())
{
LL_WARNS() << "The file '" << strings_file << "' is not found" << LL_ENDL;
crash_reason = "The file '" + strings_file + "' is not found";
}
else
{
@ -2996,24 +3001,23 @@ void LLAppViewer::initStrings()
int rc = LLFile::stat(strings_path_full, &st);
if (rc != 0)
{
LL_WARNS() << "The file '" << strings_path_full << "' failed to get status. Error code: " << rc << LL_ENDL;
crash_reason = "The file '" + strings_path_full + "' failed to get status. Error code: " + std::to_string(rc);
}
else if (S_ISDIR(st.st_mode))
{
LL_WARNS() << "The filename '" << strings_path_full << "' is a directory name" << LL_ENDL;
crash_reason = "The filename '" + strings_path_full + "' is a directory name";
}
else
{
LL_WARNS() << "The filename '" << strings_path_full << "' doesn't seem to be a regular file name" << LL_ENDL;
crash_reason = "The filename '" + strings_path_full + "' doesn't seem to be a regular file name";
}
}
// initial check to make sure files are there failed
gDirUtilp->dumpCurrentDirectories(LLError::LEVEL_WARN);
LLError::LLUserWarningMsg::showMissingFiles();
LL_ERRS() << "Viewer failed to find localization and UI files."
<< " Please reinstall viewer from https://secondlife.com/support/downloads"
<< " and contact https://support.secondlife.com if issue persists after reinstall." << LL_ENDL;
LL_ERRS() << "Viewer failed to open some of localization and UI files."
<< " " << crash_reason << "." << LL_ENDL;
}
LLTransUtil::parseStrings(strings_file, default_trans_args);
LLTransUtil::parseLanguageStrings("language_settings.xml");
@ -4254,7 +4258,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 ;
}
@ -4573,11 +4577,32 @@ void LLAppViewer::saveFinalSnapshot()
}
}
static const char PRODUCTION_CACHE_FORMAT_STRING[] = "%s.%s";
static const char GRID_CACHE_FORMAT_STRING[] = "%s.%s.%s";
std::string get_name_cache_filename(const std::string &base_file, const std::string& extention)
{
std::string filename;
std::string path(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, base_file));
if (LLGridManager::getInstance()->isInProductionGrid())
{
filename = llformat(PRODUCTION_CACHE_FORMAT_STRING, path.c_str(), extention.c_str());
}
else
{
// NOTE: The inventory cache filenames now include the grid name.
// Add controls against directory traversal or problematic pathname lengths
// if your viewer uses grid names from an untrusted source.
const std::string& grid_id_str = LLGridManager::getInstance()->getGridId();
const std::string& grid_id_lower = utf8str_tolower(grid_id_str);
filename = llformat(GRID_CACHE_FORMAT_STRING, path.c_str(), grid_id_lower.c_str(), extention.c_str());
}
return filename;
}
void LLAppViewer::loadNameCache()
{
// display names cache
std::string filename =
gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "avatar_name_cache.xml");
std::string filename = get_name_cache_filename("avatar_name_cache", "xml");
LL_INFOS("AvNameCache") << filename << LL_ENDL;
llifstream name_cache_stream(filename.c_str());
if(name_cache_stream.is_open())
@ -4592,8 +4617,8 @@ void LLAppViewer::loadNameCache()
if (!gCacheName) return;
std::string name_cache;
name_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "name.cache");
// is there a reason for the "cache" extention?
std::string name_cache = get_name_cache_filename("name", "cache");
llifstream cache_file(name_cache.c_str());
if(cache_file.is_open())
{
@ -4604,8 +4629,7 @@ void LLAppViewer::loadNameCache()
void LLAppViewer::saveNameCache()
{
// display names cache
std::string filename =
gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "avatar_name_cache.xml");
std::string filename = get_name_cache_filename("avatar_name_cache", "xml");
llofstream name_cache_stream(filename.c_str());
if(name_cache_stream.is_open())
{
@ -4615,8 +4639,7 @@ void LLAppViewer::saveNameCache()
// real names cache
if (gCacheName)
{
std::string name_cache;
name_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "name.cache");
std::string name_cache = get_name_cache_filename("name", "cache");
llofstream cache_file(name_cache.c_str());
if(cache_file.is_open())
{
@ -4894,6 +4917,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;
}
@ -5330,12 +5367,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");
@ -5362,6 +5396,7 @@ void LLAppViewer::idleNetwork()
F32 total_time = 0.0f;
{
bool needs_drain = false;
LockMessageChecker lmc(gMessageSystem);
while (lmc.checkAllMessages(frame_count, gServicePump))
{
@ -5374,54 +5409,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);
@ -5604,6 +5629,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

View File

@ -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;

View File

@ -41,6 +41,7 @@ void clearDumpLogsDir();
struct CrashMetadata
{
std::string logFilePathname;
std::string attributesPathname;
std::string userSettingsPathname;
std::string accountSettingsPathname;
std::string staticDebugPathname;

View File

@ -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++.

View File

@ -804,14 +804,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()

View File

@ -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.

View File

@ -669,6 +669,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() };

View File

@ -240,10 +240,11 @@ void LLNotificationChiclet::setCounter(S32 counter)
bool LLNotificationChiclet::ChicletNotificationChannel::filterNotification( LLNotificationPtr notification )
{
bool displayNotification;
LLFloaterNotificationsTabbed* floater = LLFloaterNotificationsTabbed::getInstance();
if ( (notification->getName() == "ScriptDialog") // special case for scripts
// if there is no toast window for the notification, filter it
//|| (!LLNotificationWellWindow::getInstance()->findItemByID(notification->getID()))
|| (!LLFloaterNotificationsTabbed::getInstance()->findItemByID(notification->getID(), notification->getName()))
|| (floater && !floater->findItemByID(notification->getID(), notification->getName()))
)
{
displayNotification = false;

View File

@ -33,6 +33,7 @@
// viewer includes
#include "llagent.h"
#include "llagentcamera.h"
#include "llcriticaldamp.h"
#include "llface.h"
#include "lllightconstants.h"
@ -778,6 +779,14 @@ bool LLDrawable::updateMove()
makeActive();
// #3256 force undampened movement for attached objects in mouselook
// to prevent animation bork for linkset with animated parts
if (!isRoot() && gAgentCamera.cameraMouselook() &&
!mVObjp->isRiggedMesh() && mVObjp->getAvatar() && mVObjp->getAvatar()->isSelf())
{
return updateMoveUndamped();
}
return isState(MOVE_UNDAMPED) ? updateMoveUndamped() : updateMoveDamped();
}

View File

@ -176,154 +176,133 @@ void LLDrawPoolWater::renderPostDeferred(S32 pass)
light_diffuse *= (1.5f + (6.f * ground_proj_sq));
}
// set up normal maps filtering
for (auto norm_map : mWaterNormp)
{
if (norm_map) norm_map->setFilteringOption(has_normal_mips ? LLTexUnit::TFO_ANISOTROPIC : LLTexUnit::TFO_POINT);
}
LLTexUnit::eTextureFilterOptions filter_mode = has_normal_mips ? LLTexUnit::TFO_ANISOTROPIC : LLTexUnit::TFO_POINT;
LLColor4 specular(sun_up ? psky->getSunlightColor() : psky->getMoonlightColor());
F32 phase_time = (F32) LLFrameTimer::getElapsedSeconds() * 0.5f;
LLGLSLShader *shader = nullptr;
// two passes, first with standard water shader bound, second with edge water shader bound
for (int edge = 0; edge < 2; edge++)
// One pass, one of two shaders. Void water and region water share state.
// There isn't a good reason anymore to really have void water run in a separate pass.
// It also just introduced a bunch of weird state consistency stuff that we really don't need.
// Not to mention, re-binding the the same shader and state for that shader is kind of wasteful.
// - Geenz 2025-02-11
// select shader
if (underwater)
{
// select shader
if (underwater)
{
shader = &gUnderWaterProgram;
}
else
{
if (edge)
{
shader = &gWaterEdgeProgram;
}
else
{
shader = &gWaterProgram;
}
}
gPipeline.bindDeferredShader(*shader, nullptr, &gPipeline.mWaterDis);
//bind normal map
S32 bumpTex = shader->enableTexture(LLViewerShaderMgr::BUMP_MAP);
S32 bumpTex2 = shader->enableTexture(LLViewerShaderMgr::BUMP_MAP2);
LLViewerTexture* tex_a = mWaterNormp[0];
LLViewerTexture* tex_b = mWaterNormp[1];
F32 blend_factor = (F32)pwater->getBlendFactor();
gGL.getTexUnit(bumpTex)->unbind(LLTexUnit::TT_TEXTURE);
gGL.getTexUnit(bumpTex2)->unbind(LLTexUnit::TT_TEXTURE);
if (tex_a && (!tex_b || (tex_a == tex_b)))
{
gGL.getTexUnit(bumpTex)->bind(tex_a);
blend_factor = 0; // only one tex provided, no blending
}
else if (tex_b && !tex_a)
{
gGL.getTexUnit(bumpTex)->bind(tex_b);
blend_factor = 0; // only one tex provided, no blending
}
else if (tex_b != tex_a)
{
gGL.getTexUnit(bumpTex)->bind(tex_a);
gGL.getTexUnit(bumpTex2)->bind(tex_b);
}
shader->bindTexture(LLShaderMgr::WATER_EXCLUSIONTEX, &gPipeline.mWaterExclusionMask);
// bind reflection texture from RenderTarget
S32 screentex = shader->enableTexture(LLShaderMgr::WATER_SCREENTEX);
F32 screenRes[] = { 1.f / gGLViewport[2], 1.f / gGLViewport[3] };
shader->uniform2fv(LLShaderMgr::DEFERRED_SCREEN_RES, 1, screenRes);
shader->uniform1f(LLShaderMgr::BLEND_FACTOR, blend_factor);
F32 fog_density = pwater->getModifiedWaterFogDensity(underwater);
if (screentex > -1)
{
shader->uniform1f(LLShaderMgr::WATER_FOGDENSITY, fog_density);
gGL.getTexUnit(screentex)->bind(&gPipeline.mWaterDis);
}
if (mShaderLevel == 1)
{
fog_color.mV[VALPHA] = (F32)(log(fog_density) / log(2));
}
F32 water_height = environment.getWaterHeight();
F32 camera_height = LLViewerCamera::getInstance()->getOrigin().mV[2];
shader->uniform1f(LLShaderMgr::WATER_WATERHEIGHT, camera_height - water_height);
shader->uniform1f(LLShaderMgr::WATER_TIME, phase_time);
shader->uniform3fv(LLShaderMgr::WATER_EYEVEC, 1, LLViewerCamera::getInstance()->getOrigin().mV);
shader->uniform4fv(LLShaderMgr::SPECULAR_COLOR, 1, specular.mV);
shader->uniform4fv(LLShaderMgr::WATER_FOGCOLOR, 1, fog_color.mV);
shader->uniform3fv(LLShaderMgr::WATER_FOGCOLOR_LINEAR, 1, fog_color_linear.mV);
shader->uniform3fv(LLShaderMgr::WATER_SPECULAR, 1, light_diffuse.mV);
shader->uniform1f(LLShaderMgr::WATER_SPECULAR_EXP, light_exp);
shader->uniform2fv(LLShaderMgr::WATER_WAVE_DIR1, 1, pwater->getWave1Dir().mV);
shader->uniform2fv(LLShaderMgr::WATER_WAVE_DIR2, 1, pwater->getWave2Dir().mV);
shader->uniform3fv(LLShaderMgr::WATER_LIGHT_DIR, 1, light_dir.mV);
shader->uniform3fv(LLShaderMgr::WATER_NORM_SCALE, 1, pwater->getNormalScale().mV);
shader->uniform1f(LLShaderMgr::WATER_FRESNEL_SCALE, pwater->getFresnelScale());
shader->uniform1f(LLShaderMgr::WATER_FRESNEL_OFFSET, pwater->getFresnelOffset());
shader->uniform1f(LLShaderMgr::WATER_BLUR_MULTIPLIER, pwater->getBlurMultiplier());
F32 sunAngle = llmax(0.f, light_dir.mV[1]);
F32 scaledAngle = 1.f - sunAngle;
shader->uniform1i(LLShaderMgr::SUN_UP_FACTOR, sun_up ? 1 : 0);
shader->uniform1f(LLShaderMgr::WATER_SUN_ANGLE, sunAngle);
shader->uniform1f(LLShaderMgr::WATER_SCALED_ANGLE, scaledAngle);
shader->uniform1f(LLShaderMgr::WATER_SUN_ANGLE2, 0.1f + 0.2f * sunAngle);
shader->uniform1i(LLShaderMgr::WATER_EDGE_FACTOR, edge ? 1 : 0);
// SL-15861 This was changed from getRotatedLightNorm() as it was causing
// lightnorm in shaders\class1\windlight\atmosphericsFuncs.glsl in have inconsistent additive lighting for 180 degrees of the FOV.
LLVector4 rotated_light_direction = LLEnvironment::instance().getClampedLightNorm();
shader->uniform3fv(LLViewerShaderMgr::LIGHTNORM, 1, rotated_light_direction.mV);
shader->uniform3fv(LLShaderMgr::WL_CAMPOSLOCAL, 1, LLViewerCamera::getInstance()->getOrigin().mV);
if (LLViewerCamera::getInstance()->cameraUnderWater())
{
shader->uniform1f(LLShaderMgr::WATER_REFSCALE, pwater->getScaleBelow());
}
else
{
shader->uniform1f(LLShaderMgr::WATER_REFSCALE, pwater->getScaleAbove());
}
LLGLDisable cullface(GL_CULL_FACE);
pushWaterPlanes(edge);
shader->disableTexture(LLShaderMgr::ENVIRONMENT_MAP, LLTexUnit::TT_CUBE_MAP);
shader->disableTexture(LLShaderMgr::WATER_SCREENTEX);
shader->disableTexture(LLShaderMgr::BUMP_MAP);
// clean up
gPipeline.unbindDeferredShader(*shader);
gGL.getTexUnit(bumpTex)->unbind(LLTexUnit::TT_TEXTURE);
gGL.getTexUnit(bumpTex2)->unbind(LLTexUnit::TT_TEXTURE);
shader = &gUnderWaterProgram;
}
else
{
shader = &gWaterProgram;
}
gGL.getTexUnit(0)->activate();
gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE);
gPipeline.bindDeferredShader(*shader, nullptr, &gPipeline.mWaterDis);
LLViewerTexture* tex_a = mWaterNormp[0];
LLViewerTexture* tex_b = mWaterNormp[1];
F32 blend_factor = (F32)pwater->getBlendFactor();
if (tex_a && (!tex_b || (tex_a == tex_b)))
{
shader->bindTexture(LLViewerShaderMgr::BUMP_MAP, tex_a);
tex_a->setFilteringOption(filter_mode);
blend_factor = 0; // only one tex provided, no blending
}
else if (tex_b && !tex_a)
{
shader->bindTexture(LLViewerShaderMgr::BUMP_MAP, tex_b);
tex_b->setFilteringOption(filter_mode);
blend_factor = 0; // only one tex provided, no blending
}
else if (tex_b != tex_a)
{
shader->bindTexture(LLViewerShaderMgr::BUMP_MAP, tex_a);
tex_a->setFilteringOption(filter_mode);
shader->bindTexture(LLViewerShaderMgr::BUMP_MAP2, tex_b);
tex_b->setFilteringOption(filter_mode);
}
shader->bindTexture(LLShaderMgr::WATER_EXCLUSIONTEX, &gPipeline.mWaterExclusionMask);
shader->uniform1f(LLShaderMgr::BLEND_FACTOR, blend_factor);
F32 fog_density = pwater->getModifiedWaterFogDensity(underwater);
shader->bindTexture(LLShaderMgr::WATER_SCREENTEX, &gPipeline.mWaterDis);
if (mShaderLevel == 1)
{
fog_color.mV[VALPHA] = (F32)(log(fog_density) / log(2));
}
F32 water_height = environment.getWaterHeight();
F32 camera_height = LLViewerCamera::getInstance()->getOrigin().mV[2];
shader->uniform1f(LLShaderMgr::WATER_WATERHEIGHT, camera_height - water_height);
shader->uniform1f(LLShaderMgr::WATER_TIME, phase_time);
shader->uniform3fv(LLShaderMgr::WATER_EYEVEC, 1, LLViewerCamera::getInstance()->getOrigin().mV);
shader->uniform3fv(LLShaderMgr::WATER_SPECULAR, 1, light_diffuse.mV);
shader->uniform2fv(LLShaderMgr::WATER_WAVE_DIR1, 1, pwater->getWave1Dir().mV);
shader->uniform2fv(LLShaderMgr::WATER_WAVE_DIR2, 1, pwater->getWave2Dir().mV);
shader->uniform3fv(LLShaderMgr::WATER_LIGHT_DIR, 1, light_dir.mV);
shader->uniform3fv(LLShaderMgr::WATER_NORM_SCALE, 1, pwater->getNormalScale().mV);
shader->uniform1f(LLShaderMgr::WATER_FRESNEL_SCALE, pwater->getFresnelScale());
shader->uniform1f(LLShaderMgr::WATER_FRESNEL_OFFSET, pwater->getFresnelOffset());
shader->uniform1f(LLShaderMgr::WATER_BLUR_MULTIPLIER, fmaxf(0, pwater->getBlurMultiplier()) * 2);
static LLStaticHashedString s_exposure("exposure");
static LLStaticHashedString tonemap_mix("tonemap_mix");
static LLStaticHashedString tonemap_type("tonemap_type");
static LLCachedControl<F32> exposure(gSavedSettings, "RenderExposure", 1.f);
F32 e = llclamp(exposure(), 0.5f, 4.f);
static LLCachedControl<bool> should_auto_adjust(gSavedSettings, "RenderSkyAutoAdjustLegacy", false);
shader->uniform1f(s_exposure, e);
static LLCachedControl<U32> tonemap_type_setting(gSavedSettings, "RenderTonemapType", 0U);
shader->uniform1i(tonemap_type, tonemap_type_setting);
shader->uniform1f(tonemap_mix, psky->getTonemapMix(should_auto_adjust()));
F32 sunAngle = llmax(0.f, light_dir.mV[1]);
F32 scaledAngle = 1.f - sunAngle;
shader->uniform1i(LLShaderMgr::SUN_UP_FACTOR, sun_up ? 1 : 0);
// SL-15861 This was changed from getRotatedLightNorm() as it was causing
// lightnorm in shaders\class1\windlight\atmosphericsFuncs.glsl in have inconsistent additive lighting for 180 degrees of the FOV.
LLVector4 rotated_light_direction = LLEnvironment::instance().getClampedLightNorm();
shader->uniform3fv(LLViewerShaderMgr::LIGHTNORM, 1, rotated_light_direction.mV);
shader->uniform3fv(LLShaderMgr::WL_CAMPOSLOCAL, 1, LLViewerCamera::getInstance()->getOrigin().mV);
if (LLViewerCamera::getInstance()->cameraUnderWater())
{
shader->uniform1f(LLShaderMgr::WATER_REFSCALE, pwater->getScaleBelow());
}
else
{
shader->uniform1f(LLShaderMgr::WATER_REFSCALE, pwater->getScaleAbove());
}
LLGLDisable cullface(GL_CULL_FACE);
// Only push the water planes once.
// Previously we did this twice: once for void water and one for region water.
// However, the void water and region water shaders are the same exact shader.
// They also had the same exact state with the sole exception setting an edge water flag.
// That flag was not actually used anywhere in the shaders.
// - Geenz 2025-02-11
pushWaterPlanes(0);
// clean up
gPipeline.unbindDeferredShader(*shader);
gGL.setColorMask(true, false);
}
@ -333,22 +312,18 @@ void LLDrawPoolWater::pushWaterPlanes(int pass)
LLVOWater* water = nullptr;
for (LLFace* const& face : mDrawFace)
{
if (!face)
continue;
water = static_cast<LLVOWater*>(face->getViewerObject());
if (!water)
continue;
if ((bool)pass == (bool)water->getIsEdgePatch())
face->renderIndexed();
// Note non-void water being drawn, updates required
// Previously we had some logic to determine if this pass was also our water edge pass.
// Now we only have one pass. Check if we're doing a region water plane or void water plane.
// - Geenz 2025-02-11
if (!water->getIsEdgePatch())
{
face->renderIndexed();
// Note non-void water being drawn, updates required
if (!pass) // SL-16461 remove !LLPipeline::sUseOcclusion check
{
sNeedsReflectionUpdate = true;
sNeedsDistortionUpdate = true;
}
sNeedsReflectionUpdate = true;
sNeedsDistortionUpdate = true;
}
}
}

View File

@ -670,6 +670,7 @@ std::unique_ptr<std::vector<std::string>> LLFilePicker::navOpenFilterProc(ELoadF
break;
case FFLOAD_HDRI:
allowedv->push_back("exr");
case FFLOAD_MODEL:
case FFLOAD_COLLADA:
allowedv->push_back("dae");
break;

View File

@ -33,6 +33,7 @@
#include "llagentui.h"
#include "llbase64.h"
#include "llcallbacklist.h"
#include "lldate.h"
#include "llenvironment.h"
#include "llimagejpeg.h"
#include "llmediactrl.h"
@ -692,7 +693,15 @@ void LLFloater360Capture::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent
);
// execute the command on the page
mWebBrowser->getMediaPlugin()->executeJavaScript(cmd);
LLPluginClassMedia* plugin = mWebBrowser->getMediaPlugin();
if (plugin)
{
plugin->executeJavaScript(cmd);
}
else
{
LL_WARNS("360Capture") << "No media plugin found" << LL_ENDL;
}
}
}
break;
@ -773,7 +782,15 @@ void LLFloater360Capture::onSaveLocalBtn()
// send it to the browser instance, triggering the equirectangular capture
// process and complimentary offer to save the image
mWebBrowser->getMediaPlugin()->executeJavaScript(cmd);
LLPluginClassMedia* plugin = mWebBrowser->getMediaPlugin();
if (plugin)
{
plugin->executeJavaScript(cmd);
}
else
{
LL_WARNS("360Capture") << "No media plugin found" << LL_ENDL;
}
}
// We capture all 6 images sequentially and if parts of the world are moving
@ -863,15 +880,7 @@ const std::string LLFloater360Capture::generate_proposed_filename()
filename << "_";
// add in the current HH-MM-SS (with leading 0's) so users can easily save many shots in same folder
std::time_t cur_epoch = std::time(nullptr);
std::tm* tm_time = std::localtime(&cur_epoch);
filename << std::setfill('0') << std::setw(4) << (tm_time->tm_year + 1900);
filename << std::setfill('0') << std::setw(2) << (tm_time->tm_mon + 1);
filename << std::setfill('0') << std::setw(2) << tm_time->tm_mday;
filename << "_";
filename << std::setfill('0') << std::setw(2) << tm_time->tm_hour;
filename << std::setfill('0') << std::setw(2) << tm_time->tm_min;
filename << std::setfill('0') << std::setw(2) << tm_time->tm_sec;
filename << LLDate::now().toLocalDateString("%Y%m%d_%H%M%S");
// the unusual way we save the output image (originates in the
// embedded browser and not the C++ code) means that the system

View File

@ -31,6 +31,7 @@
#include "llagent.h"
#include "llagentui.h"
#include "llcombobox.h"
#include "llfloaterreg.h"
#include "llinventoryfunctions.h"
#include "llinventoryobserver.h"
#include "lllandmarkactions.h"
@ -389,6 +390,7 @@ void LLFloaterCreateLandmark::setItem(const uuid_set_t& items)
{
mItem = item;
mAssetID = mItem->getAssetUUID();
mParentID = mItem->getParentUUID();
setVisibleAndFrontmost(true);
break;
}
@ -418,8 +420,7 @@ void LLFloaterCreateLandmark::updateItem(const uuid_set_t& items, U32 mask)
closeFloater();
}
LLUUID folder_id = mFolderCombo->getValue().asUUID();
if (folder_id != mItem->getParentUUID())
if (mParentID != mItem->getParentUUID())
{
// user moved landmark in inventory,
// assume that we are done all other changes should already be commited

View File

@ -69,6 +69,7 @@ private:
LLTextEditor* mNotesEditor;
LLUUID mLandmarksID;
LLUUID mAssetID;
LLUUID mParentID;
LLLandmarksInventoryObserver* mInventoryObserver;
LLPointer<LLInventoryItem> mItem;

View File

@ -57,8 +57,7 @@ static const S32 USED_EMOJIS_IMAGE_INDEX = 0x23F2;
// https://www.compart.com/en/unicode/U+1F6D1
static const S32 EMPTY_LIST_IMAGE_INDEX = 0x1F6D1;
// The following categories should follow the required alphabetic order
static const std::string RECENTLY_USED_CATEGORY = "1 recently used";
static const std::string FREQUENTLY_USED_CATEGORY = "2 frequently used";
static const std::string FREQUENTLY_USED_CATEGORY = "frequently used";
// Floater state related variables
static std::list<llwchar> sRecentlyUsed;
@ -445,11 +444,10 @@ void LLFloaterEmojiPicker::fillGroups()
params.name = "all_categories";
createGroupButton(params, rect, ALL_EMOJIS_IMAGE_INDEX);
// Create group and button for "Recently used" and/or "Frequently used"
if (!sRecentlyUsed.empty() || !sFrequentlyUsed.empty())
// Create group and button for "Frequently used"
if (!sFrequentlyUsed.empty())
{
std::map<std::string, std::vector<LLEmojiSearchResult>> cats;
fillCategoryRecentlyUsed(cats);
fillCategoryFrequentlyUsed(cats);
if (!cats.empty())
@ -482,40 +480,6 @@ void LLFloaterEmojiPicker::fillGroups()
resizeGroupButtons();
}
void LLFloaterEmojiPicker::fillCategoryRecentlyUsed(std::map<std::string, std::vector<LLEmojiSearchResult>>& cats)
{
if (sRecentlyUsed.empty())
return;
std::vector<LLEmojiSearchResult> emojis;
// In case of empty mFilterPattern we'd use sRecentlyUsed directly
if (!mFilterPattern.empty())
{
// List all emojis in "Recently used"
const LLEmojiDictionary::emoji2descr_map_t& emoji2descr = LLEmojiDictionary::instance().getEmoji2Descr();
std::size_t begin, end;
for (llwchar emoji : sRecentlyUsed)
{
auto e2d = emoji2descr.find(emoji);
if (e2d != emoji2descr.end() && !e2d->second->ShortCodes.empty())
{
for (const std::string& shortcode : e2d->second->ShortCodes)
{
if (LLEmojiDictionary::searchInShortCode(begin, end, shortcode, mFilterPattern))
{
emojis.emplace_back(emoji, shortcode, begin, end);
}
}
}
}
if (emojis.empty())
return;
}
cats.emplace(std::make_pair(RECENTLY_USED_CATEGORY, emojis));
}
void LLFloaterEmojiPicker::fillCategoryFrequentlyUsed(std::map<std::string, std::vector<LLEmojiSearchResult>>& cats)
{
if (sFrequentlyUsed.empty())
@ -756,7 +720,6 @@ void LLFloaterEmojiPicker::fillEmojisCategory(const std::vector<LLEmojiSearchRes
{
// Place the category title
std::string title =
category == RECENTLY_USED_CATEGORY ? getString("title_for_recently_used") :
category == FREQUENTLY_USED_CATEGORY ? getString("title_for_frequently_used") :
isupper(category.front()) ? category : LLStringUtil::capitalize(category);
LLEmojiGridDivider* div = new LLEmojiGridDivider(row_panel_params, title);
@ -769,21 +732,7 @@ void LLFloaterEmojiPicker::fillEmojisCategory(const std::vector<LLEmojiSearchRes
{
const LLEmojiDictionary::emoji2descr_map_t& emoji2descr = LLEmojiDictionary::instance().getEmoji2Descr();
LLEmojiSearchResult emoji { 0, "", 0, 0 };
if (category == RECENTLY_USED_CATEGORY)
{
for (llwchar code : sRecentlyUsed)
{
const LLEmojiDictionary::emoji2descr_map_t::const_iterator& e2d = emoji2descr.find(code);
if (e2d != emoji2descr.end() && !e2d->second->ShortCodes.empty())
{
emoji.Character = code;
emoji.String = e2d->second->ShortCodes.front();
createEmojiIcon(emoji, category, row_panel_params, row_list_params, icon_params,
icon_rect, max_icons, bg, row, icon_index);
}
}
}
else if (category == FREQUENTLY_USED_CATEGORY)
if (category == FREQUENTLY_USED_CATEGORY)
{
for (const auto& code : sFrequentlyUsed)
{

View File

@ -60,7 +60,6 @@ public:
private:
void initialize();
void fillGroups();
void fillCategoryRecentlyUsed(std::map<std::string, std::vector<LLEmojiSearchResult>>& cats);
void fillCategoryFrequentlyUsed(std::map<std::string, std::vector<LLEmojiSearchResult>>& cats);
void fillGroupEmojis(std::map<std::string, std::vector<LLEmojiSearchResult>>& cats, U32 index);
void createGroupButton(LLButton::Params& params, const LLRect& rect, llwchar emoji);

View File

@ -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));

View File

@ -115,12 +115,12 @@ bool LLFloaterPerformance::postBuild()
mHUDList = mHUDsPanel->getChild<LLNameListCtrl>("hud_list");
mHUDList->setNameListType(LLNameListCtrl::SPECIAL);
mHUDList->setHoverIconName("StopReload_Off");
mHUDList->setIconClickedCallback(boost::bind(&LLFloaterPerformance::detachItem, this, _1));
mHUDList->setIconClickedCallback(boost::bind(&LLFloaterPerformance::detachObject, this, _1));
mObjectList = mComplexityPanel->getChild<LLNameListCtrl>("obj_list");
mObjectList->setNameListType(LLNameListCtrl::SPECIAL);
mObjectList->setHoverIconName("StopReload_Off");
mObjectList->setIconClickedCallback(boost::bind(&LLFloaterPerformance::detachItem, this, _1));
mObjectList->setIconClickedCallback(boost::bind(&LLFloaterPerformance::detachObject, this, _1));
mSettingsPanel->getChild<LLButton>("advanced_btn")->setCommitCallback(boost::bind(&LLFloaterPerformance::onClickAdvanced, this));
mSettingsPanel->getChild<LLButton>("defaults_btn")->setCommitCallback(boost::bind(&LLFloaterPerformance::onClickDefaults, this));
@ -527,9 +527,13 @@ void LLFloaterPerformance::setFPSText()
mTextFPSLabel->setValue(fps_text);
}
void LLFloaterPerformance::detachItem(const LLUUID& item_id)
void LLFloaterPerformance::detachObject(const LLUUID& obj_id)
{
LLAppearanceMgr::instance().removeItemFromAvatar(item_id);
LLViewerObject* obj = gObjectList.findObject(obj_id);
if (obj)
{
LLAppearanceMgr::instance().removeItemFromAvatar(obj->getAttachmentItemID());
}
}
void LLFloaterPerformance::onClickAdvanced()

View File

@ -48,7 +48,7 @@ public:
void hidePanels();
void showAutoadjustmentsPanel();
void detachItem(const LLUUID& item_id);
void detachObject(const LLUUID& obj_id);
void onAvatarListRightClick(LLUICtrl* ctrl, S32 x, S32 y);

View File

@ -329,6 +329,7 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key)
mCommitCallbackRegistrar.add("Pref.AutoAdjustments", boost::bind(&LLFloaterPreference::onClickAutoAdjustments, this));
mCommitCallbackRegistrar.add("Pref.HardwareDefaults", boost::bind(&LLFloaterPreference::setHardwareDefaults, this));
mCommitCallbackRegistrar.add("Pref.AvatarImpostorsEnable", boost::bind(&LLFloaterPreference::onAvatarImpostorsEnable, this));
mCommitCallbackRegistrar.add("Pref.UpdateIndirectMaxNonImpostors", boost::bind(&LLFloaterPreference::updateMaxNonImpostors, this));
mCommitCallbackRegistrar.add("Pref.UpdateIndirectMaxComplexity", boost::bind(&LLFloaterPreference::updateMaxComplexity, this));
mCommitCallbackRegistrar.add("Pref.RenderOptionUpdate", boost::bind(&LLFloaterPreference::onRenderOptionEnable, this));
mCommitCallbackRegistrar.add("Pref.WindowedMod", boost::bind(&LLFloaterPreference::onCommitWindowedMode, this));
@ -360,6 +361,7 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key)
LLAvatarPropertiesProcessor::getInstance()->addObserver( gAgent.getID(), this );
mComplexityChangedSignal = gSavedSettings.getControl("RenderAvatarMaxComplexity")->getCommitSignal()->connect(boost::bind(&LLFloaterPreference::updateComplexityText, this));
mImpostorsChangedSignal = gSavedSettings.getControl("RenderAvatarMaxNonImpostors")->getSignal()->connect(boost::bind(&LLFloaterPreference::updateIndirectMaxNonImpostors, this, _2));
mCommitCallbackRegistrar.add("Pref.ClearLog", boost::bind(&LLConversationLog::onClearLog, &LLConversationLog::instance()));
mCommitCallbackRegistrar.add("Pref.DeleteTranscripts", boost::bind(&LLFloaterPreference::onDeleteTranscripts, this));
@ -542,7 +544,12 @@ void LLFloaterPreference::onDoNotDisturbResponseChanged()
LLFloaterPreference::~LLFloaterPreference()
{
LLConversationLog::instance().removeObserver(this);
if (LLAvatarPropertiesProcessor::instanceExists())
{
LLAvatarPropertiesProcessor::getInstance()->removeObserver(gAgent.getID(), this);
}
mComplexityChangedSignal.disconnect();
mImpostorsChangedSignal.disconnect();
}
void LLFloaterPreference::draw()
@ -1287,6 +1294,9 @@ void LLAvatarComplexityControls::setIndirectMaxArc()
void LLFloaterPreference::refresh()
{
LLPanel::refresh();
setMaxNonImpostorsText(
gSavedSettings.getU32("RenderAvatarMaxNonImpostors"),
getChild<LLTextBox>("IndirectMaxNonImpostorsText", true));
LLAvatarComplexityControls::setText(
gSavedSettings.getU32("RenderAvatarMaxComplexity"),
getChild<LLTextBox>("IndirectMaxComplexityText", true));
@ -1561,6 +1571,44 @@ void LLAvatarComplexityControls::setRenderTimeText(F32 value, LLTextBox* text_bo
}
}
void LLFloaterPreference::updateMaxNonImpostors()
{
// Called when the IndirectMaxNonImpostors control changes
// Responsible for fixing the slider label (IndirectMaxNonImpostorsText) and setting RenderAvatarMaxNonImpostors
LLSliderCtrl* ctrl = getChild<LLSliderCtrl>("IndirectMaxNonImpostors", true);
U32 value = ctrl->getValue().asInteger();
if (0 == value || LLVOAvatar::NON_IMPOSTORS_MAX_SLIDER <= value)
{
value = 0;
}
gSavedSettings.setU32("RenderAvatarMaxNonImpostors", value);
LLVOAvatar::updateImpostorRendering(value); // make it effective immediately
setMaxNonImpostorsText(value, getChild<LLTextBox>("IndirectMaxNonImpostorsText"));
}
void LLFloaterPreference::updateIndirectMaxNonImpostors(const LLSD& newvalue)
{
U32 value = newvalue.asInteger();
if ((value != 0) && (value != gSavedSettings.getU32("IndirectMaxNonImpostors")))
{
gSavedSettings.setU32("IndirectMaxNonImpostors", value);
}
setMaxNonImpostorsText(value, getChild<LLTextBox>("IndirectMaxNonImpostorsText"));
}
void LLFloaterPreference::setMaxNonImpostorsText(U32 value, LLTextBox* text_box)
{
if (0 == value)
{
text_box->setText(LLTrans::getString("no_limit"));
}
else
{
text_box->setText(llformat("%d", value));
}
}
void LLFloaterPreference::updateMaxComplexity()
{
// Called when the IndirectMaxComplexity control changes

View File

@ -206,6 +206,9 @@ private:
void onDeleteTranscripts();
void onDeleteTranscriptsResponse(const LLSD& notification, const LLSD& response);
void updateDeleteTranscriptsButton();
void updateMaxNonImpostors();
void updateIndirectMaxNonImpostors(const LLSD& newvalue);
void setMaxNonImpostorsText(U32 value, LLTextBox* text_box);
void updateMaxComplexity();
void updateComplexityText();
static bool loadFromFilename(const std::string& filename, std::map<std::string, std::string> &label_map);
@ -234,6 +237,7 @@ private:
std::unique_ptr< ll::prefs::SearchData > mSearchData;
bool mSearchDataDirty;
boost::signals2::connection mImpostorsChangedSignal;
boost::signals2::connection mComplexityChangedSignal;
void onUpdateFilterTerm( bool force = false );

View File

@ -51,6 +51,8 @@ LLFloaterPreferenceGraphicsAdvanced::LLFloaterPreferenceGraphicsAdvanced(const L
mCommitCallbackRegistrar.add("Pref.Cancel", boost::bind(&LLFloaterPreferenceGraphicsAdvanced::onBtnCancel, this, _2));
mCommitCallbackRegistrar.add("Pref.OK", boost::bind(&LLFloaterPreferenceGraphicsAdvanced::onBtnOK, this, _2));
mImpostorsChangedSignal = gSavedSettings.getControl("RenderAvatarMaxNonImpostors")->getSignal()->connect(boost::bind(&LLFloaterPreferenceGraphicsAdvanced::updateIndirectMaxNonImpostors, this, _2));
}
LLFloaterPreferenceGraphicsAdvanced::~LLFloaterPreferenceGraphicsAdvanced()
@ -58,7 +60,6 @@ LLFloaterPreferenceGraphicsAdvanced::~LLFloaterPreferenceGraphicsAdvanced()
mComplexityChangedSignal.disconnect();
mComplexityModeChangedSignal.disconnect();
mLODFactorChangedSignal.disconnect();
mNumImpostorsChangedSignal.disconnect();
}
bool LLFloaterPreferenceGraphicsAdvanced::postBuild()
@ -254,8 +255,8 @@ void LLFloaterPreferenceGraphicsAdvanced::updateIndirectMaxNonImpostors(const LL
if ((value != 0) && (value != gSavedSettings.getU32("IndirectMaxNonImpostors")))
{
gSavedSettings.setU32("IndirectMaxNonImpostors", value);
setMaxNonImpostorsText(value, getChild<LLTextBox>("IndirectMaxNonImpostorsText"));
}
setMaxNonImpostorsText(value, getChild<LLTextBox>("IndirectMaxNonImpostorsText"));
}
void LLFloaterPreferenceGraphicsAdvanced::setMaxNonImpostorsText(U32 value, LLTextBox* text_box)

View File

@ -61,6 +61,7 @@ protected:
void onBtnOK(const LLSD& userdata);
void onBtnCancel(const LLSD& userdata);
boost::signals2::connection mImpostorsChangedSignal;
boost::signals2::connection mComplexityChangedSignal;
boost::signals2::connection mComplexityModeChangedSignal;
boost::signals2::connection mLODFactorChangedSignal;

View File

@ -851,6 +851,13 @@ void LLPanelRegionInfo::initCtrl(const std::string& name)
getChild<LLUICtrl>(name)->setCommitCallback(boost::bind(&LLPanelRegionInfo::onChangeAnything, this));
}
void LLPanelRegionInfo::initAndSetTexCtrl(LLTextureCtrl*& ctrl, const std::string& name)
{
ctrl = findChild<LLTextureCtrl>(name);
if (ctrl)
ctrl->setOnSelectCallback([this](LLUICtrl* ctrl, const LLSD& param){ onChangeAnything(); });
}
template<typename CTRL>
void LLPanelRegionInfo::initAndSetCtrl(CTRL*& ctrl, const std::string& name)
{
@ -1580,7 +1587,7 @@ bool LLPanelRegionTerrainInfo::postBuild()
for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
{
initAndSetCtrl(mTextureDetailCtrl[i], llformat("texture_detail_%d", i));
initAndSetTexCtrl(mTextureDetailCtrl[i], llformat("texture_detail_%d", i));
if (mTextureDetailCtrl[i])
{
mTextureDetailCtrl[i]->setBakeTextureEnabled(false);

View File

@ -159,6 +159,7 @@ public:
protected:
void initCtrl(const std::string& name);
template<typename CTRL> void initAndSetCtrl(CTRL*& ctrl, const std::string& name);
void initAndSetTexCtrl(LLTextureCtrl*& ctrl, const std::string& name);
// Returns true if update sent and apply button should be
// disabled.

View File

@ -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);

View File

@ -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;
LL_PROFILE_GPU_ZONE("hero manager update");
llassert(!gCubeSnapshot); // assert a snapshot is not in progress

View File

@ -144,6 +144,7 @@ private:
std::vector<LLPointer<LLVOVolume>> mHeroVOList;
LLPointer<LLVOVolume> mNearestHero;
// Part of a hacky workaround to fix #3331.
bool mInitialized = false;
};

View File

@ -650,14 +650,6 @@ void LLIMProcessing::processNewMessage(LLUUID from_id,
asset_type = (LLAssetType::EType)(atoi((*(iter++)).c_str()));
iter++; // wearable type if applicable, otherwise asset type
item_name = std::string((*(iter++)).c_str());
// Note There is more elements in 'tokens' ...
for (int i = 0; i < 6; i++)
{
LL_WARNS() << *(iter++) << LL_ENDL;
iter++;
}
}
}
else

View File

@ -3550,6 +3550,7 @@ void LLIMMgr::inviteToSession(
&& voice_invite && "VoiceInviteQuestionDefault" == question_type)
{
LL_INFOS("IMVIEW") << "Rejecting voice call from initiating muted resident " << caller_name << LL_ENDL;
payload["voice_channel_info"] = voice_channel_info;
LLIncomingCallDialog::processCallResponse(1, payload);
return;
}
@ -3598,6 +3599,7 @@ void LLIMMgr::inviteToSession(
send_do_not_disturb_message(gMessageSystem, caller_id, session_id);
}
// silently decline the call
payload["voice_channel_info"] = voice_channel_info;
LLIncomingCallDialog::processCallResponse(1, payload);
return;
}

View File

@ -844,7 +844,7 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id,
disabled_items.push_back(std::string("Copy"));
}
if (isAgentInventory() && !single_folder_root)
if (isAgentInventory() && !single_folder_root && !isMarketplaceListingsFolder())
{
items.push_back(std::string("New folder from selected"));
items.push_back(std::string("Subfolder Separator"));
@ -5315,7 +5315,7 @@ void LLFolderBridge::dropToMyOutfits(LLInventoryCategory* inv_cat, LLPointer<LLI
// Note: creation will take time, so passing folder id to callback is slightly unreliable,
// but so is collecting and passing descendants' ids
inventory_func_type func = boost::bind(&LLFolderBridge::outfitFolderCreatedCallback, this, inv_cat->getUUID(), _1, cb);
inventory_func_type func = boost::bind(outfitFolderCreatedCallback, inv_cat->getUUID(), _1, cb, mInventoryPanel);
gInventory.createNewCategory(dest_id,
LLFolderType::FT_OUTFIT,
inv_cat->getName(),
@ -5323,11 +5323,25 @@ void LLFolderBridge::dropToMyOutfits(LLInventoryCategory* inv_cat, LLPointer<LLI
inv_cat->getThumbnailUUID());
}
void LLFolderBridge::outfitFolderCreatedCallback(LLUUID cat_source_id, LLUUID cat_dest_id, LLPointer<LLInventoryCallback> cb)
void LLFolderBridge::outfitFolderCreatedCallback(LLUUID cat_source_id,
LLUUID cat_dest_id,
LLPointer<LLInventoryCallback> cb,
LLHandle<LLInventoryPanel> inventory_panel)
{
LLInventoryModel::cat_array_t* categories;
LLInventoryModel::item_array_t* items;
getInventoryModel()->getDirectDescendentsOf(cat_source_id, categories, items);
LLInventoryPanel* panel = inventory_panel.get();
if (!panel)
{
return;
}
LLInventoryModel* model = panel->getModel();
if (!model)
{
return;
}
model->getDirectDescendentsOf(cat_source_id, categories, items);
LLInventoryObject::const_object_list_t link_array;

View File

@ -378,7 +378,10 @@ public:
static void staticFolderOptionsMenu();
protected:
void outfitFolderCreatedCallback(LLUUID cat_source_id, LLUUID cat_dest_id, LLPointer<LLInventoryCallback> cb);
static void outfitFolderCreatedCallback(LLUUID cat_source_id,
LLUUID cat_dest_id,
LLPointer<LLInventoryCallback> cb,
LLHandle<LLInventoryPanel> inventory_panel);
void callback_pasteFromClipboard(const LLSD& notification, const LLSD& response);
void perform_pasteFromClipboard();
void gatherMessage(std::string& message, S32 depth, LLError::ELevel log_level);

View File

@ -30,6 +30,7 @@
#include "llaisapi.h"
#include "llagent.h"
#include "llappviewer.h"
#include "llappearancemgr.h"
#include "llcallbacklist.h"
#include "llinventorymodel.h"
#include "llinventorypanel.h"
@ -470,6 +471,22 @@ void LLInventoryModelBackgroundFetch::fetchCOF(nullary_func_t callback)
callback();
LLUUID cat_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
LLInventoryModelBackgroundFetch::getInstance()->onAISFolderCalback(cat_id, id, FT_DEFAULT);
if (id.notNull())
{
// COF might have fetched base outfit folder through a link, but it hasn't
// fetched base outfit's content, which doesn't nessesary match COF,
// so make sure it's up to date
LLUUID baseoutfit_id = LLAppearanceMgr::getInstance()->getBaseOutfitUUID();
if (baseoutfit_id.notNull())
{
LLViewerInventoryCategory* cat = gInventory.getCategory(baseoutfit_id);
if (!cat || cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN)
{
LLInventoryModelBackgroundFetch::getInstance()->fetchFolderAndLinks(baseoutfit_id, no_op);
}
}
}
});
// start idle loop to track completion

View File

@ -579,7 +579,7 @@ void LLInventoryPanel::itemChanged(const LLUUID& item_id, U32 mask, const LLInve
{
if (model_item && view_item && viewmodel_item)
{
const LLUUID& idp = viewmodel_item->getUUID();
const LLUUID idp = viewmodel_item->getUUID();
view_item->destroyView();
removeItemID(idp);
}

View File

@ -892,6 +892,11 @@ bool LLLogChat::isTranscriptFileFound(std::string fullname)
return result;
}
std::string LLLogChat::getGroupChatSuffix()
{
return GROUP_CHAT_SUFFIX;
}
//*TODO mark object's names in a special way so that they will be distinguishable form avatar name
//which are more strict by its nature (only firstname and secondname)
//Example, an object's name can be written like "Object <actual_object's_name>"

View File

@ -127,6 +127,8 @@ public:
static bool isAdHocTranscriptExist(std::string file_name);
static bool isTranscriptFileFound(std::string fullname);
static std::string getGroupChatSuffix();
bool historyThreadsFinished(LLUUID session_id);
LLLoadHistoryThread* getLoadHistoryThread(LLUUID session_id);
LLDeleteHistoryThread* getDeleteHistoryThread(LLUUID session_id);

File diff suppressed because it is too large Load Diff

View File

@ -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,22 +416,13 @@ public:
public:
LLVolumeParams mMeshParams;
S32 mLOD;
F32 mScore;
LODRequest(const LLVolumeParams& mesh_params, S32 lod)
: RequestStats(), mMeshParams(mesh_params), mLOD(lod), mScore(0.f)
: RequestStats(), mMeshParams(mesh_params), mLOD(lod)
{
}
};
struct CompareScoreGreater
{
bool operator()(const LODRequest& lhs, const LODRequest& rhs)
{
return lhs.mScore > rhs.mScore; // greatest = first
}
};
class UUIDBasedRequest : public RequestStats
{
public:
@ -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;

View File

@ -92,10 +92,18 @@ void LLHandlerUtil::logToIM(const EInstantMessage& session_type,
from = SYSTEM_FROM;
}
// Build a new format username or firstname_lastname for legacy names
// to use it for a history log filename.
std::string user_name = LLCacheName::buildUsername(session_name);
LLIMModel::instance().logToFile(user_name, from, from_id, message);
std::string file_name;
if (session_type == IM_SESSION_GROUP_START)
{
file_name = session_name + LLLogChat::getGroupChatSuffix();
}
else
{
// Build a new format username or firstname_lastname for legacy names
// to use it for a history log filename.
file_name = LLCacheName::buildUsername(session_name);
}
LLIMModel::instance().logToFile(file_name, from, from_id, message);
}
else
{

Some files were not shown because too many files have changed in this diff Show More