Merge branch 'release/2025.03' into rye/forevermac
commit
e0d14e02e1
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -190,7 +190,6 @@ private:
|
|||
mutable LLFontBitmapCache* mFontBitmapCachep;
|
||||
|
||||
mutable S32 mRenderGlyphCount;
|
||||
mutable S32 mAddGlyphCount;
|
||||
};
|
||||
|
||||
#endif // LL_FONTFREETYPE_H
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ public:
|
|||
bool attachNothing = false;
|
||||
bool hasHeroProbes = false;
|
||||
bool isPBRTerrain = false;
|
||||
bool hasTonemap = false;
|
||||
};
|
||||
|
||||
// ============= Structure for caching shader uniforms ===============
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
7.1.12
|
||||
7.1.13
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ void clearDumpLogsDir();
|
|||
struct CrashMetadata
|
||||
{
|
||||
std::string logFilePathname;
|
||||
std::string attributesPathname;
|
||||
std::string userSettingsPathname;
|
||||
std::string accountSettingsPathname;
|
||||
std::string staticDebugPathname;
|
||||
|
|
|
|||
|
|
@ -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++.
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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() };
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ private:
|
|||
LLTextEditor* mNotesEditor;
|
||||
LLUUID mLandmarksID;
|
||||
LLUUID mAssetID;
|
||||
LLUUID mParentID;
|
||||
|
||||
LLLandmarksInventoryObserver* mInventoryObserver;
|
||||
LLPointer<LLInventoryItem> mItem;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 );
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -144,6 +144,7 @@ private:
|
|||
std::vector<LLPointer<LLVOVolume>> mHeroVOList;
|
||||
LLPointer<LLVOVolume> mNearestHero;
|
||||
|
||||
|
||||
// Part of a hacky workaround to fix #3331.
|
||||
bool mInitialized = false;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>"
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue