Merge branch 'release/2025.03' into rye/forevermac
commit
e0d14e02e1
|
|
@ -42,7 +42,7 @@ jobs:
|
||||||
needs: setup
|
needs: setup
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
runner: [windows-large, macos-12-large]
|
runner: [windows-large, macos-15-xlarge]
|
||||||
configuration: ${{ fromJSON(needs.setup.outputs.configurations) }}
|
configuration: ${{ fromJSON(needs.setup.outputs.configurations) }}
|
||||||
runs-on: ${{ matrix.runner }}
|
runs-on: ${{ matrix.runner }}
|
||||||
outputs:
|
outputs:
|
||||||
|
|
@ -64,7 +64,7 @@ jobs:
|
||||||
# autobuild-package.xml.
|
# autobuild-package.xml.
|
||||||
AUTOBUILD_VCS_INFO: "true"
|
AUTOBUILD_VCS_INFO: "true"
|
||||||
AUTOBUILD_VSVER: "170"
|
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.
|
# Ensure that Linden viewer builds engage Bugsplat.
|
||||||
BUGSPLAT_DB: ${{ needs.setup.outputs.bugsplat_db }}
|
BUGSPLAT_DB: ${{ needs.setup.outputs.bugsplat_db }}
|
||||||
build_coverity: false
|
build_coverity: false
|
||||||
|
|
|
||||||
|
|
@ -23,4 +23,4 @@ jobs:
|
||||||
path-to-signatures: signatures.json
|
path-to-signatures: signatures.json
|
||||||
remote-organization-name: secondlife
|
remote-organization-name: secondlife
|
||||||
remote-repository-name: cla-signatures
|
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 )
|
set( USE_AUTOBUILD_3P ON )
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (NOT DEFINED CMAKE_CXX_STANDARD)
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
endif()
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
include(Variables)
|
include(Variables)
|
||||||
include(BuildVersion)
|
include(BuildVersion)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -799,7 +799,6 @@ void LLAvatarAppearance::buildCharacter()
|
||||||
bool status = loadAvatar();
|
bool status = loadAvatar();
|
||||||
stop_glerror();
|
stop_glerror();
|
||||||
|
|
||||||
// gPrintMessagesThisFrame = true;
|
|
||||||
LL_DEBUGS() << "Avatar load took " << timer.getElapsedTimeF32() << " seconds." << LL_ENDL;
|
LL_DEBUGS() << "Avatar load took " << timer.getElapsedTimeF32() << " seconds." << LL_ENDL;
|
||||||
|
|
||||||
if (!status)
|
if (!status)
|
||||||
|
|
|
||||||
|
|
@ -981,7 +981,7 @@ void LLPolyMesh::initializeForMorph()
|
||||||
LLVector4a::memcpyNonAliased16((F32*) mScaledNormals, (F32*) mSharedData->mBaseNormals, sizeof(LLVector4a) * mSharedData->mNumVertices);
|
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*) mBinormals, (F32*) mSharedData->mBaseNormals, sizeof(LLVector4a) * mSharedData->mNumVertices);
|
||||||
LLVector4a::memcpyNonAliased16((F32*) mScaledBinormals, (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)
|
for (S32 i = 0; i < mSharedData->mNumVertices; ++i)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -79,11 +79,8 @@ public:
|
||||||
// shadow base-class string() method with UTF-8 aware method
|
// shadow base-class string() method with UTF-8 aware method
|
||||||
std::string string() const
|
std::string string() const
|
||||||
{
|
{
|
||||||
// Short of forbidden type punning, I see no way to avoid copying this
|
auto u8 = super::u8string();
|
||||||
// std::u8string to a std::string.
|
return std::string(u8.begin(), u8.end());
|
||||||
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() };
|
|
||||||
}
|
}
|
||||||
// On Posix systems, where value_type is already char, this operator
|
// On Posix systems, where value_type is already char, this operator
|
||||||
// std::string() method shadows the base class operator string_type()
|
// std::string() method shadows the base class operator string_type()
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,7 @@ public:
|
||||||
LLRunner& getRunner() { return mRunner; }
|
LLRunner& getRunner() { return mRunner; }
|
||||||
|
|
||||||
#ifdef LL_WINDOWS
|
#ifdef LL_WINDOWS
|
||||||
virtual void reportCrashToBugsplat(void* pExcepInfo /*EXCEPTION_POINTERS*/) { }
|
virtual bool reportCrashToBugsplat(void* pExcepInfo /*EXCEPTION_POINTERS*/) { return false; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
|
||||||
|
|
@ -310,18 +310,25 @@ namespace
|
||||||
|
|
||||||
static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific
|
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
|
// C++ exception, go on
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// handle it
|
// handle it, convert to std::exception
|
||||||
return EXCEPTION_EXECUTE_HANDLER;
|
return EXCEPTION_EXECUTE_HANDLER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sehandle(const LLCoros::callable_t& callable)
|
void sehandle(const LLCoros::callable_t& callable)
|
||||||
|
|
@ -379,6 +386,7 @@ void LLCoros::toplevel(std::string name, callable_t callable)
|
||||||
// viewer will carry on.
|
// viewer will carry on.
|
||||||
LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << name));
|
LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << name));
|
||||||
}
|
}
|
||||||
|
#ifndef LL_WINDOWS
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
// Stash any OTHER kind of uncaught exception in the rethrow() queue
|
// 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;
|
<< name << LL_ENDL;
|
||||||
LLCoros::instance().saveException(name, std::current_exception());
|
LLCoros::instance().saveException(name, std::current_exception());
|
||||||
}
|
}
|
||||||
|
#endif // ! LL_WINDOWS
|
||||||
}
|
}
|
||||||
|
|
||||||
//static
|
//static
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,15 @@ std::string LLDate::asRFC1123() const
|
||||||
return toHTTPDateString (std::string ("%A, %d %b %Y %H:%M:%S GMT"));
|
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
|
std::string LLDate::toHTTPDateString (std::string fmt) const
|
||||||
{
|
{
|
||||||
LL_PROFILE_ZONE_SCOPED;
|
LL_PROFILE_ZONE_SCOPED;
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,7 @@ public:
|
||||||
std::string asRFC1123() const;
|
std::string asRFC1123() const;
|
||||||
void toStream(std::ostream&) 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;
|
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;
|
std::string toHTTPDateString (std::string fmt) const;
|
||||||
static std::string toHTTPDateString (tm * gmt, std::string fmt);
|
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)
|
void LLSettingsBase::lerpVector2(LLVector2& a, const LLVector2& b, F32 mix)
|
||||||
{
|
{
|
||||||
a.mV[0] = lerp(a.mV[0], b.mV[0], mix);
|
a.mV[0] = lerp(a.mV[0], b.mV[0], mix);
|
||||||
|
|
|
||||||
|
|
@ -348,13 +348,8 @@ protected:
|
||||||
LLSettingsBase();
|
LLSettingsBase();
|
||||||
LLSettingsBase(const LLSD setting);
|
LLSettingsBase(const LLSD setting);
|
||||||
|
|
||||||
static LLSD settingValidation(LLSD settings);
|
|
||||||
|
|
||||||
typedef std::set<std::string> stringset_t;
|
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
|
// combining settings maps where it can based on mix rate
|
||||||
// @settings initial value (mix==0)
|
// @settings initial value (mix==0)
|
||||||
// @other target value (mix==1)
|
// @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");
|
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_SUN_ID("32bfbcea-24b1-fb9d-1ef9-48a28a63730f"); // dataserver
|
||||||
static const LLUUID DEFAULT_MOON_ID("d07f6eed-b96a-47cd-b51d-400ad4a1c428"); // 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 LLUUID DEFAULT_ASSET_ID;
|
||||||
|
|
||||||
|
static const F32 DEFAULT_AUTO_ADJUST_PROBE_AMBIANCE;
|
||||||
static F32 sAutoAdjustProbeAmbiance;
|
static F32 sAutoAdjustProbeAmbiance;
|
||||||
|
|
||||||
typedef PTR_NAMESPACE::shared_ptr<LLSettingsSky> ptr_t;
|
typedef PTR_NAMESPACE::shared_ptr<LLSettingsSky> ptr_t;
|
||||||
|
|
|
||||||
|
|
@ -301,12 +301,12 @@ LLCoprocedurePool::LLCoprocedurePool(const std::string &poolName, size_t size):
|
||||||
mPoolSize(size),
|
mPoolSize(size),
|
||||||
mActiveCoprocsCount(0),
|
mActiveCoprocsCount(0),
|
||||||
mPending(0),
|
mPending(0),
|
||||||
mPendingCoprocs(std::make_shared<CoprocQueue_t>(LLCoprocedureManager::DEFAULT_QUEUE_SIZE)),
|
|
||||||
mHTTPPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID),
|
mHTTPPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID),
|
||||||
mCoroMapping()
|
mCoroMapping()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
mPendingCoprocs = std::make_shared<CoprocQueue_t>(LLCoprocedureManager::DEFAULT_QUEUE_SIZE);
|
||||||
// store in our LLTempBoundListener so that when the LLCoprocedurePool is
|
// store in our LLTempBoundListener so that when the LLCoprocedurePool is
|
||||||
// destroyed, we implicitly disconnect from this LLEventPump
|
// destroyed, we implicitly disconnect from this LLEventPump
|
||||||
// Monitores application status
|
// Monitores application status
|
||||||
|
|
@ -339,6 +339,11 @@ LLCoprocedurePool::LLCoprocedurePool(const std::string &poolName, size_t size):
|
||||||
|
|
||||||
llassert(0); // Fix Me! Ignoring missing listener!
|
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)
|
for (size_t count = 0; count < mPoolSize; ++count)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -295,7 +295,15 @@ void HttpCoroHandler::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespons
|
||||||
}
|
}
|
||||||
else
|
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);
|
buildStatusEntry(response, status, result);
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,6 @@
|
||||||
#include "lltimer.h"
|
#include "lltimer.h"
|
||||||
#include "llhost.h"
|
#include "llhost.h"
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
LLPacketBuffer::LLPacketBuffer(const LLHost &host, const char *datap, const S32 size) : mHost(host)
|
LLPacketBuffer::LLPacketBuffer(const LLHost &host, const char *datap, const S32 size) : mHost(host)
|
||||||
{
|
{
|
||||||
mSize = 0;
|
mSize = 0;
|
||||||
|
|
@ -41,7 +39,7 @@ LLPacketBuffer::LLPacketBuffer(const LLHost &host, const char *datap, const S32
|
||||||
|
|
||||||
if (size > NET_BUFFER_SIZE)
|
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
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -51,7 +49,6 @@ LLPacketBuffer::LLPacketBuffer(const LLHost &host, const char *datap, const S32
|
||||||
mSize = size;
|
mSize = size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LLPacketBuffer::LLPacketBuffer (S32 hSocket)
|
LLPacketBuffer::LLPacketBuffer (S32 hSocket)
|
||||||
|
|
@ -59,18 +56,29 @@ LLPacketBuffer::LLPacketBuffer (S32 hSocket)
|
||||||
init(hSocket);
|
init(hSocket);
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
LLPacketBuffer::~LLPacketBuffer ()
|
LLPacketBuffer::~LLPacketBuffer ()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////
|
void LLPacketBuffer::init(S32 hSocket)
|
||||||
|
|
||||||
void LLPacketBuffer::init (S32 hSocket)
|
|
||||||
{
|
{
|
||||||
mSize = receive_packet(hSocket, mData);
|
mSize = receive_packet(hSocket, mData);
|
||||||
mHost = ::get_sender();
|
mHost = ::get_sender();
|
||||||
mReceivingIF = ::get_receiving_interface();
|
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:
|
public:
|
||||||
LLPacketBuffer(const LLHost &host, const char *datap, const S32 size);
|
LLPacketBuffer(const LLHost &host, const char *datap, const S32 size);
|
||||||
LLPacketBuffer(S32 hSocket); // receive a packet
|
LLPacketBuffer(S32 hSocket); // receive a packet
|
||||||
~LLPacketBuffer();
|
~LLPacketBuffer();
|
||||||
|
|
||||||
S32 getSize() const { return mSize; }
|
S32 getSize() const { return mSize; }
|
||||||
const char *getData() const { return mData; }
|
const char *getData() const { return mData; }
|
||||||
LLHost getHost() const { return mHost; }
|
LLHost getHost() const { return mHost; }
|
||||||
LLHost getReceivingInterface() const { return mReceivingIF; }
|
LLHost getReceivingInterface() const { return mReceivingIF; }
|
||||||
|
|
||||||
void init(S32 hSocket);
|
void init(S32 hSocket);
|
||||||
|
void init(const char* buffer, S32 data_size, const LLHost& host);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
char mData[NET_BUFFER_SIZE]; // packet data /* Flawfinder : ignore */
|
char mData[NET_BUFFER_SIZE]; // packet data /* Flawfinder : ignore */
|
||||||
S32 mSize; // size of buffer in bytes
|
S32 mSize; // size of buffer in bytes
|
||||||
LLHost mHost; // source/dest IP and port
|
LLHost mHost; // source/dest IP and port
|
||||||
LLHost mReceivingIF; // source/dest IP and port
|
LLHost mReceivingIF; // source/dest IP and port
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* @file llpacketring.cpp
|
* @file llpacketring.cpp
|
||||||
* @brief implementation of LLPacketRing class for a packet.
|
* @brief implementation of LLPacketRing class.
|
||||||
*
|
*
|
||||||
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
||||||
* Second Life Viewer Source Code
|
* Second Life Viewer Source Code
|
||||||
|
|
@ -43,313 +43,44 @@
|
||||||
#include "message.h"
|
#include "message.h"
|
||||||
#include "u64.h"
|
#include "u64.h"
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////
|
constexpr S16 MAX_BUFFER_RING_SIZE = 1024;
|
||||||
LLPacketRing::LLPacketRing () :
|
constexpr S16 DEFAULT_BUFFER_RING_SIZE = 256;
|
||||||
mUseInThrottle(false),
|
|
||||||
mUseOutThrottle(false),
|
LLPacketRing::LLPacketRing ()
|
||||||
mInThrottle(256000.f),
|
: mPacketRing(DEFAULT_BUFFER_RING_SIZE, nullptr)
|
||||||
mOutThrottle(64000.f),
|
|
||||||
mActualBitsIn(0),
|
|
||||||
mActualBitsOut(0),
|
|
||||||
mMaxBufferLength(64000),
|
|
||||||
mInBufferLength(0),
|
|
||||||
mOutBufferLength(0),
|
|
||||||
mDropPercentage(0.0f),
|
|
||||||
mPacketsToDrop(0x0)
|
|
||||||
{
|
{
|
||||||
|
LLHost invalid_host;
|
||||||
|
for (size_t i = 0; i < mPacketRing.size(); ++i)
|
||||||
|
{
|
||||||
|
mPacketRing[i] = new LLPacketBuffer(invalid_host, nullptr, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////
|
|
||||||
LLPacketRing::~LLPacketRing ()
|
LLPacketRing::~LLPacketRing ()
|
||||||
{
|
{
|
||||||
cleanup();
|
for (auto packet : mPacketRing)
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////
|
|
||||||
void LLPacketRing::cleanup ()
|
|
||||||
{
|
|
||||||
LLPacketBuffer *packetp;
|
|
||||||
|
|
||||||
while (!mReceiveQueue.empty())
|
|
||||||
{
|
{
|
||||||
packetp = mReceiveQueue.front();
|
delete packet;
|
||||||
delete packetp;
|
|
||||||
mReceiveQueue.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!mSendQueue.empty())
|
|
||||||
{
|
|
||||||
packetp = mSendQueue.front();
|
|
||||||
delete packetp;
|
|
||||||
mSendQueue.pop();
|
|
||||||
}
|
}
|
||||||
|
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 LLPacketRing::receivePacket (S32 socket, char *datap)
|
||||||
{
|
{
|
||||||
S32 packet_size = 0;
|
bool drop = computeDrop();
|
||||||
|
return (mNumBufferedPackets > 0) ?
|
||||||
// If using the throttle, simulate a limited size input buffer.
|
receiveOrDropBufferedPacket(datap, drop) :
|
||||||
if (mUseInThrottle)
|
receiveOrDropPacket(socket, datap, drop);
|
||||||
{
|
|
||||||
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 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())
|
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];
|
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->atype = ADDRESS_IPV4;
|
||||||
socks_header->frag = 0;
|
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,
|
headered_send_buffer,
|
||||||
buf_size + SOCKS_HEADER_SIZE,
|
data_size + SOCKS_HEADER_SIZE,
|
||||||
LLProxy::getInstance()->getUDPProxy().getAddress(),
|
LLProxy::getInstance()->getUDPProxy().getAddress(),
|
||||||
LLProxy::getInstance()->getUDPProxy().getPort());
|
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$
|
* $/LicenseInfo$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LL_LLPACKETRING_H
|
#pragma once
|
||||||
#define LL_LLPACKETRING_H
|
|
||||||
|
|
||||||
#include <queue>
|
#include <vector>
|
||||||
|
|
||||||
#include "llhost.h"
|
#include "llhost.h"
|
||||||
#include "llpacketbuffer.h"
|
#include "llpacketbuffer.h"
|
||||||
#include "llproxy.h"
|
|
||||||
#include "llthrottle.h"
|
#include "llthrottle.h"
|
||||||
#include "net.h"
|
|
||||||
|
|
||||||
class LLPacketRing
|
class LLPacketRing
|
||||||
{
|
{
|
||||||
|
|
@ -42,60 +40,70 @@ public:
|
||||||
LLPacketRing();
|
LLPacketRing();
|
||||||
~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 dropPackets(U32);
|
||||||
void setDropPercentage (F32 percent_to_drop);
|
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();
|
S32 getActualInBytes() const { return mActualBytesIn; }
|
||||||
inline LLHost getLastReceivingInterface();
|
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 getNumBufferedPackets() const { return (S32)(mNumBufferedPackets); }
|
||||||
S32 getAndResetActualOutBits() { S32 bits = mActualBitsOut; mActualBitsOut = 0; return bits;}
|
S32 getNumBufferedBytes() const { return mNumBufferedBytes; }
|
||||||
|
S32 getNumDroppedPackets() const { return mNumDroppedPacketsTotal + mNumDroppedPackets; }
|
||||||
|
|
||||||
|
void dumpPacketRingStats();
|
||||||
protected:
|
protected:
|
||||||
bool mUseInThrottle;
|
// returns 'true' if we should intentionally drop a packet
|
||||||
bool mUseOutThrottle;
|
bool computeDrop();
|
||||||
|
|
||||||
// For simulating a lower-bandwidth connection - BPS
|
// returns packet_size of received packet, zero or less if no packet found
|
||||||
LLThrottle mInThrottle;
|
S32 receiveOrDropPacket(S32 socket, char *datap, bool drop);
|
||||||
LLThrottle mOutThrottle;
|
S32 receiveOrDropBufferedPacket(char *datap, bool drop);
|
||||||
|
|
||||||
S32 mActualBitsIn;
|
// returns packet_size of packet buffered
|
||||||
S32 mActualBitsOut;
|
S32 bufferInboundPacket(S32 socket);
|
||||||
S32 mMaxBufferLength; // How much data can we queue up before dropping data.
|
|
||||||
S32 mInBufferLength; // Current incoming buffer length
|
|
||||||
S32 mOutBufferLength; // Current outgoing buffer length
|
|
||||||
|
|
||||||
F32 mDropPercentage; // % of packets to drop
|
// returns 'true' if ring was expanded
|
||||||
U32 mPacketsToDrop; // drop next n packets
|
bool expandRing();
|
||||||
|
|
||||||
std::queue<LLPacketBuffer *> mReceiveQueue;
|
protected:
|
||||||
std::queue<LLPacketBuffer *> mSendQueue;
|
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 mLastSender;
|
||||||
LLHost mLastReceivingIF;
|
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;
|
return mLastSender;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline LLHost LLPacketRing::getLastReceivingInterface()
|
inline LLHost LLPacketRing::getLastReceivingInterface() const
|
||||||
{
|
{
|
||||||
return mLastReceivingIF;
|
return mLastReceivingIF;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
||||||
|
|
@ -465,7 +465,7 @@ void LLProxy::applyProxySettings(CURL* handle)
|
||||||
/**
|
/**
|
||||||
* @brief Send one TCP packet and receive one in return.
|
* @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.
|
* operation would impact the operation of the viewer.
|
||||||
*
|
*
|
||||||
* @param handle_ptr Pointer to a connected LLSocket of type STREAM_TCP.
|
* @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;
|
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);
|
rv = apr_socket_send(apr_socket, dataout, &outlen);
|
||||||
if (APR_SUCCESS != rv)
|
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
|
// UseCircuitCode is allowed in even from an invalid circuit, so that
|
||||||
// we can toss circuits around.
|
// we can toss circuits around.
|
||||||
if(
|
else if (
|
||||||
valid_packet &&
|
|
||||||
!cdp &&
|
!cdp &&
|
||||||
(mTemplateMessageReader->getMessageName() !=
|
(mTemplateMessageReader->getMessageName() !=
|
||||||
_PREHASH_UseCircuitCode))
|
_PREHASH_UseCircuitCode))
|
||||||
|
|
@ -667,8 +666,7 @@ bool LLMessageSystem::checkMessages(LockMessageChecker&, S64 frame_count )
|
||||||
valid_packet = false;
|
valid_packet = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(
|
if ( valid_packet &&
|
||||||
valid_packet &&
|
|
||||||
cdp &&
|
cdp &&
|
||||||
!cdp->getTrusted() &&
|
!cdp->getTrusted() &&
|
||||||
mTemplateMessageReader->isTrusted())
|
mTemplateMessageReader->isTrusted())
|
||||||
|
|
@ -680,7 +678,7 @@ bool LLMessageSystem::checkMessages(LockMessageChecker&, S64 frame_count )
|
||||||
valid_packet = false;
|
valid_packet = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( valid_packet )
|
if ( valid_packet )
|
||||||
{
|
{
|
||||||
logValidMsg(cdp, host, recv_reliable, recv_resent, acks>0 );
|
logValidMsg(cdp, host, recv_reliable, recv_resent, acks>0 );
|
||||||
valid_packet = mTemplateMessageReader->readMessage(buffer, host);
|
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
|
// Check to see if we need to print debug info
|
||||||
if ((mt_sec - mCircuitPrintTime) > mCircuitPrintFreq)
|
if ((mt_sec - mCircuitPrintTime) > mCircuitPrintFreq)
|
||||||
{
|
{
|
||||||
|
mPacketRing.dumpPacketRingStats();
|
||||||
dumpCircuitInfo();
|
dumpCircuitInfo();
|
||||||
mCircuitPrintTime = mt_sec;
|
mCircuitPrintTime = mt_sec;
|
||||||
}
|
}
|
||||||
|
|
@ -821,6 +820,11 @@ void LLMessageSystem::processAcks(LockMessageChecker&, F32 collect_time)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
S32 LLMessageSystem::drainUdpSocket()
|
||||||
|
{
|
||||||
|
return mPacketRing.drainSocket(mSocket);
|
||||||
|
}
|
||||||
|
|
||||||
void LLMessageSystem::copyMessageReceivedToSend()
|
void LLMessageSystem::copyMessageReceivedToSend()
|
||||||
{
|
{
|
||||||
// NOTE: babbage: switch builder to match reader to avoid
|
// NOTE: babbage: switch builder to match reader to avoid
|
||||||
|
|
|
||||||
|
|
@ -417,6 +417,9 @@ public:
|
||||||
bool checkMessages(LockMessageChecker&, S64 frame_count = 0 );
|
bool checkMessages(LockMessageChecker&, S64 frame_count = 0 );
|
||||||
void processAcks(LockMessageChecker&, F32 collect_time = 0.f);
|
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 isMessageFast(const char *msg);
|
||||||
bool isMessage(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* LOOPBACK_ADDRESS_STRING = "127.0.0.1";
|
||||||
const char* BROADCAST_ADDRESS_STRING = "255.255.255.255";
|
const char* BROADCAST_ADDRESS_STRING = "255.255.255.255";
|
||||||
|
|
||||||
#if LL_DARWIN
|
const int SEND_BUFFER_SIZE = 200000;
|
||||||
// macOS returns an error when trying to set these to 400000. Smaller values succeed.
|
const int RECEIVE_BUFFER_SIZE = 800000;
|
||||||
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
|
|
||||||
|
|
||||||
// universal functions (cross-platform)
|
// universal functions (cross-platform)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,7 @@ void LLCubeMapArray::allocate(U32 resolution, U32 components, U32 count, bool us
|
||||||
bind(0);
|
bind(0);
|
||||||
free_cur_tex_image();
|
free_cur_tex_image();
|
||||||
|
|
||||||
U32 format = components == 4 ? GL_RGBA16F : GL_RGB16F;
|
U32 format = components == 4 ? GL_RGBA16F : GL_R11F_G11F_B10F;
|
||||||
if (!hdr)
|
if (!hdr)
|
||||||
{
|
{
|
||||||
format = components == 4 ? GL_RGBA8 : GL_RGB8;
|
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;
|
mBitmapHeight = image_height;
|
||||||
|
|
||||||
S32 num_components = getNumComponents(bitmap_type);
|
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;
|
bitmap_num = static_cast<U32>(mImageRawVec[bitmap_idx].size()) - 1;
|
||||||
|
|
||||||
LLImageRaw* image_raw = getImageRaw(bitmap_type, bitmap_num);
|
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.
|
// 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);
|
LLImageGL* image_gl = getImageGL(bitmap_type, bitmap_num);
|
||||||
|
|
||||||
// Start at beginning of the new image.
|
// 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;
|
bitmap_num = getNumBitmaps(bitmap_type) - 1;
|
||||||
|
|
||||||
mCurrentOffsetX[bitmap_idx] += width + 1;
|
mCurrentOffsetX[bitmap_idx] += width + 1;
|
||||||
|
mGeneration++;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -168,6 +169,7 @@ void LLFontBitmapCache::reset()
|
||||||
|
|
||||||
mBitmapWidth = 0;
|
mBitmapWidth = 0;
|
||||||
mBitmapHeight = 0;
|
mBitmapHeight = 0;
|
||||||
|
mGeneration++;
|
||||||
}
|
}
|
||||||
|
|
||||||
//static
|
//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; }
|
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 getBitmapWidth() const { return mBitmapWidth; }
|
||||||
S32 getBitmapHeight() const { return mBitmapHeight; }
|
S32 getBitmapHeight() const { return mBitmapHeight; }
|
||||||
|
S32 getCacheGeneration() const { return mGeneration; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static U32 getNumComponents(EFontGlyphType bitmap_type);
|
static U32 getNumComponents(EFontGlyphType bitmap_type);
|
||||||
|
|
@ -74,6 +75,7 @@ private:
|
||||||
S32 mCurrentOffsetY[static_cast<U32>(EFontGlyphType::Count)] = { 1 };
|
S32 mCurrentOffsetY[static_cast<U32>(EFontGlyphType::Count)] = { 1 };
|
||||||
S32 mMaxCharWidth = 0;
|
S32 mMaxCharWidth = 0;
|
||||||
S32 mMaxCharHeight = 0;
|
S32 mMaxCharHeight = 0;
|
||||||
|
S32 mGeneration = 0;
|
||||||
std::vector<LLPointer<LLImageRaw>> mImageRawVec[static_cast<U32>(EFontGlyphType::Count)];
|
std::vector<LLPointer<LLImageRaw>> mImageRawVec[static_cast<U32>(EFontGlyphType::Count)];
|
||||||
std::vector<LLPointer<LLImageGL>> mImageGLVec[static_cast<U32>(EFontGlyphType::Count)];
|
std::vector<LLPointer<LLImageGL>> mImageGLVec[static_cast<U32>(EFontGlyphType::Count)];
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,6 @@ LLFontFreetype::LLFontFreetype()
|
||||||
mIsFallback(false),
|
mIsFallback(false),
|
||||||
mFTFace(nullptr),
|
mFTFace(nullptr),
|
||||||
mRenderGlyphCount(0),
|
mRenderGlyphCount(0),
|
||||||
mAddGlyphCount(0),
|
|
||||||
mStyle(0),
|
mStyle(0),
|
||||||
mPointSize(0)
|
mPointSize(0)
|
||||||
{
|
{
|
||||||
|
|
@ -502,7 +501,6 @@ LLFontGlyphInfo* LLFontFreetype::addGlyphFromFont(const LLFontFreetype *fontp, l
|
||||||
S32 pos_x, pos_y;
|
S32 pos_x, pos_y;
|
||||||
U32 bitmap_num;
|
U32 bitmap_num;
|
||||||
mFontBitmapCachep->nextOpenPos(width, pos_x, pos_y, bitmap_glyph_type, bitmap_num);
|
mFontBitmapCachep->nextOpenPos(width, pos_x, pos_y, bitmap_glyph_type, bitmap_num);
|
||||||
mAddGlyphCount++;
|
|
||||||
|
|
||||||
LLFontGlyphInfo* gi = new LLFontGlyphInfo(glyph_index, requested_glyph_type);
|
LLFontGlyphInfo* gi = new LLFontGlyphInfo(glyph_index, requested_glyph_type);
|
||||||
gi->mXBitmapOffset = pos_x;
|
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)
|
if (error == FT_Err_Out_Of_Memory)
|
||||||
{
|
{
|
||||||
LLError::LLUserWarningMsg::showOutOfMemory();
|
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(
|
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_Invalid_Composite == error
|
||||||
|| (FT_Err_Ok != error && LLStringOps::isEmoji(wch)))
|
|| (FT_Err_Ok != error && LLStringOps::isEmoji(wch)))
|
||||||
{
|
{
|
||||||
glyph_index = FT_Get_Char_Index(mFTFace, '?');
|
// value~0 always corresponds to the 'missing glyph'
|
||||||
// if '?' is not present, potentially can use last index, that's supposed to be null glyph
|
error = FT_Load_Glyph(mFTFace, 0, FT_LOAD_FORCE_AUTOHINT);
|
||||||
if (glyph_index > 0)
|
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());
|
llassert_always_msg(FT_Err_Ok == error, message.c_str());
|
||||||
|
|
|
||||||
|
|
@ -190,7 +190,6 @@ private:
|
||||||
mutable LLFontBitmapCache* mFontBitmapCachep;
|
mutable LLFontBitmapCache* mFontBitmapCachep;
|
||||||
|
|
||||||
mutable S32 mRenderGlyphCount;
|
mutable S32 mRenderGlyphCount;
|
||||||
mutable S32 mAddGlyphCount;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // LL_FONTFREETYPE_H
|
#endif // LL_FONTFREETYPE_H
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,12 @@ S32 LLFontGL::getNumFaces(const std::string& filename)
|
||||||
return mFontFreetype->getNumFaces(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,
|
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
|
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();
|
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_width = 1.f / font_bitmap_cache->getBitmapWidth();
|
||||||
F32 inv_height = 1.f / font_bitmap_cache->getBitmapHeight();
|
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;
|
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;
|
static constexpr S32 GLYPH_BATCH_SIZE = 30;
|
||||||
// string can have more than one glyph per char, make sure last one can fit
|
static thread_local LLVector4a vertices[GLYPH_BATCH_SIZE * 6];
|
||||||
static constexpr S32 BUFFER_SIZE = GLYPH_BATCH_SIZE * 2;
|
static thread_local LLVector2 uvs[GLYPH_BATCH_SIZE * 6];
|
||||||
static thread_local LLVector4a vertices[BUFFER_SIZE * 6];
|
static thread_local LLColor4U colors[GLYPH_BATCH_SIZE * 6];
|
||||||
static thread_local LLVector2 uvs[BUFFER_SIZE * 6];
|
|
||||||
static thread_local LLColor4U colors[BUFFER_SIZE * 6];
|
|
||||||
|
|
||||||
LLColor4U text_color(color);
|
LLColor4U text_color(color);
|
||||||
// Preserve the transparency to render fading emojis in fading text (e.g.
|
// 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);
|
LLImageGL* font_image = font_bitmap_cache->getImageGL(bitmap_entry.first, bitmap_entry.second);
|
||||||
gGL.getTexUnit(0)->bind(font_image);
|
gGL.getTexUnit(0)->bind(font_image);
|
||||||
|
|
||||||
// For multi-byte characters just draw each time character changes
|
// For some reason it's not enough to compare by bitmap_entry.
|
||||||
// Might be overkill and might be better to detect multybyte
|
// 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;
|
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);
|
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 getNumFaces(const std::string& filename);
|
||||||
|
S32 getCacheGeneration() const;
|
||||||
|
|
||||||
S32 render(const LLWString &text, S32 begin_offset,
|
S32 render(const LLWString &text, S32 begin_offset,
|
||||||
const LLRect& rect,
|
const LLRect& rect,
|
||||||
|
|
|
||||||
|
|
@ -147,7 +147,8 @@ S32 LLFontVertexBuffer::render(
|
||||||
|| mLastVertDPI != LLFontGL::sVertDPI
|
|| mLastVertDPI != LLFontGL::sVertDPI
|
||||||
|| mLastHorizDPI != LLFontGL::sHorizDPI
|
|| mLastHorizDPI != LLFontGL::sHorizDPI
|
||||||
|| mLastOrigin != LLFontGL::sCurOrigin
|
|| mLastOrigin != LLFontGL::sCurOrigin
|
||||||
|| mLastResGeneration != LLFontGL::sResolutionGeneration)
|
|| mLastResGeneration != LLFontGL::sResolutionGeneration
|
||||||
|
|| mLastFontCacheGen != fontp->getCacheGeneration())
|
||||||
{
|
{
|
||||||
genBuffers(fontp, text, begin_offset, x, y, color, halign, valign,
|
genBuffers(fontp, text, begin_offset, x, y, color, halign, valign,
|
||||||
style, shadow, max_chars, max_pixels, right_x, use_ellipses, use_color);
|
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?
|
// todo: add a debug build assert if this triggers too often for to long?
|
||||||
mBufferList.clear();
|
mBufferList.clear();
|
||||||
|
// Save before rendreing, it can change mid-render,
|
||||||
|
// so will need to rerender previous characters
|
||||||
|
mLastFontCacheGen = fontp->getCacheGeneration();
|
||||||
|
|
||||||
gGL.beginList(&mBufferList);
|
gGL.beginList(&mBufferList);
|
||||||
mChars = fontp->render(text, begin_offset, x, y, color, halign, valign,
|
mChars = fontp->render(text, begin_offset, x, y, color, halign, valign,
|
||||||
|
|
|
||||||
|
|
@ -120,6 +120,10 @@ private:
|
||||||
S32 mLastResGeneration = 0;
|
S32 mLastResGeneration = 0;
|
||||||
LLCoordGL mLastOrigin;
|
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;
|
static bool sEnableBufferCollection;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2452,12 +2452,15 @@ void LLGLState::checkStates(GLboolean writeAlpha)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
GLint src;
|
GLint srcRGB, dstRGB, srcAlpha, dstAlpha;
|
||||||
GLint dst;
|
glGetIntegerv(GL_BLEND_SRC_RGB, &srcRGB);
|
||||||
glGetIntegerv(GL_BLEND_SRC, &src);
|
glGetIntegerv(GL_BLEND_DST_RGB, &dstRGB);
|
||||||
glGetIntegerv(GL_BLEND_DST, &dst);
|
glGetIntegerv(GL_BLEND_SRC_ALPHA, &srcAlpha);
|
||||||
llassert_always(src == GL_SRC_ALPHA);
|
glGetIntegerv(GL_BLEND_DST_ALPHA, &dstAlpha);
|
||||||
llassert_always(dst == GL_ONE_MINUS_SRC_ALPHA);
|
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
|
// disable for now until usage is consistent
|
||||||
//GLboolean colorMask[4];
|
//GLboolean colorMask[4];
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ public:
|
||||||
bool attachNothing = false;
|
bool attachNothing = false;
|
||||||
bool hasHeroProbes = false;
|
bool hasHeroProbes = false;
|
||||||
bool isPBRTerrain = false;
|
bool isPBRTerrain = false;
|
||||||
|
bool hasTonemap = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============= Structure for caching shader uniforms ===============
|
// ============= Structure for caching shader uniforms ===============
|
||||||
|
|
|
||||||
|
|
@ -330,6 +330,7 @@ S32 LLImageGL::dataFormatBits(S32 dataformat)
|
||||||
case GL_RGB: return 24;
|
case GL_RGB: return 24;
|
||||||
case GL_SRGB: return 24;
|
case GL_SRGB: return 24;
|
||||||
case GL_RGB8: return 24;
|
case GL_RGB8: return 24;
|
||||||
|
case GL_R11F_G11F_B10F: return 32;
|
||||||
case GL_RGBA: return 32;
|
case GL_RGBA: return 32;
|
||||||
case GL_RGBA8: return 32;
|
case GL_RGBA8: return 32;
|
||||||
case GL_RGB10_A2: 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!!!
|
// NOTE order of shader object attaching is VERY IMPORTANT!!!
|
||||||
if (features->hasAtmospherics)
|
if (features->hasAtmospherics)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2011,7 +2011,7 @@ void LLFolderView::onIdleUpdateMenu(void* user_data)
|
||||||
self->updateMenuOptions(menu);
|
self->updateMenuOptions(menu);
|
||||||
menu->needsArrange(); // update menu height if needed
|
menu->needsArrange(); // update menu height if needed
|
||||||
}
|
}
|
||||||
gIdleCallbacks.deleteFunction(onIdleUpdateMenu, NULL);
|
gIdleCallbacks.deleteFunction(onIdleUpdateMenu, self);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LLFolderView::isFolderSelected()
|
bool LLFolderView::isFolderSelected()
|
||||||
|
|
|
||||||
|
|
@ -3394,7 +3394,7 @@ bool LLScrollListCtrl::highlightMatchingItems(const std::string& filter_str)
|
||||||
|
|
||||||
bool res = false;
|
bool res = false;
|
||||||
|
|
||||||
setHighlightedColor(LLUIColorTable::instance().getColor("SearchableControlHighlightColor", LLColor4::red));
|
setHighlightedColor(LLUIColorTable::instance().getColor("SearchableControlHighlightColor", LLColor4::red4));
|
||||||
|
|
||||||
std::string filter_str_lc(filter_str);
|
std::string filter_str_lc(filter_str);
|
||||||
LLStringUtil::toLower(filter_str_lc);
|
LLStringUtil::toLower(filter_str_lc);
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ namespace ll
|
||||||
|
|
||||||
const LLColor4& getHighlightColor( ) const
|
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();
|
return highlight_color.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,12 @@ protected:
|
||||||
LLUIString mText;
|
LLUIString mText;
|
||||||
callback_t mClickedCallback;
|
callback_t mClickedCallback;
|
||||||
bool mShowCursorHand;
|
bool mShowCursorHand;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual std::string _getSearchText() const
|
||||||
|
{
|
||||||
|
return LLTextBase::_getSearchText() + mText.getString();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Build time optimization, generate once in .cpp file
|
// Build time optimization, generate once in .cpp file
|
||||||
|
|
|
||||||
|
|
@ -306,6 +306,7 @@ void LLXMLNode::addChild(LLXMLNodePtr& new_child)
|
||||||
// virtual
|
// virtual
|
||||||
LLXMLNodePtr LLXMLNode::createChild(const char* name, bool is_attribute)
|
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);
|
return createChild(gStringTable.addStringEntry(name), is_attribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -108,12 +108,15 @@ LLXmlTreeNode::LLXmlTreeNode( const std::string& name, LLXmlTreeNode* parent, LL
|
||||||
|
|
||||||
LLXmlTreeNode::~LLXmlTreeNode()
|
LLXmlTreeNode::~LLXmlTreeNode()
|
||||||
{
|
{
|
||||||
attribute_map_t::iterator iter;
|
for (auto& attr : mAttributes)
|
||||||
for (iter=mAttributes.begin(); iter != mAttributes.end(); iter++)
|
|
||||||
delete iter->second;
|
|
||||||
for(LLXmlTreeNode* node : mChildren)
|
|
||||||
{
|
{
|
||||||
delete node;
|
delete attr.second;
|
||||||
|
}
|
||||||
|
mAttributes.clear();
|
||||||
|
|
||||||
|
for (auto& child : mChildren)
|
||||||
|
{
|
||||||
|
delete child;
|
||||||
}
|
}
|
||||||
mChildren.clear();
|
mChildren.clear();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
7.1.12
|
7.1.13
|
||||||
|
|
|
||||||
|
|
@ -7794,6 +7794,17 @@
|
||||||
<key>Value</key>
|
<key>Value</key>
|
||||||
<integer>0</integer>
|
<integer>0</integer>
|
||||||
</map>
|
</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>
|
<key>RenderMinFreeMainMemoryThreshold</key>
|
||||||
<map>
|
<map>
|
||||||
<key>Comment</key>
|
<key>Comment</key>
|
||||||
|
|
@ -9065,6 +9076,17 @@
|
||||||
<key>Value</key>
|
<key>Value</key>
|
||||||
<integer>1</integer>
|
<integer>1</integer>
|
||||||
</map>
|
</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>
|
<key>RenderReflectionProbeResolution</key>
|
||||||
<map>
|
<map>
|
||||||
<key>Comment</key>
|
<key>Comment</key>
|
||||||
|
|
@ -11618,7 +11640,7 @@
|
||||||
<key>Type</key>
|
<key>Type</key>
|
||||||
<string>F32</string>
|
<string>F32</string>
|
||||||
<key>Value</key>
|
<key>Value</key>
|
||||||
<real>0.04</real>
|
<real>0.0095</real>
|
||||||
</map>
|
</map>
|
||||||
<key>TextureScaleMaxAreaFactor</key>
|
<key>TextureScaleMaxAreaFactor</key>
|
||||||
<map>
|
<map>
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,18 @@ vec3 atmosFragLightingLinear(vec3 light, vec3 additive, vec3 atten);
|
||||||
|
|
||||||
vec4 decodeNormal(vec4 norm);
|
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 calcLegacyDistanceAttenuation(float distance, float falloff)
|
||||||
{
|
{
|
||||||
float dist_atten = 1.0 - clamp((distance + falloff)/(1.0 + falloff), 0.0, 1.0);
|
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;
|
w += wg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vec3 clampHDRRange(vec3 color);
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
vec2 tc = vary_fragcoord.xy;
|
vec2 tc = vary_fragcoord.xy;
|
||||||
|
|
@ -120,5 +122,6 @@ void main()
|
||||||
diff /= w;
|
diff /= w;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
diff.rgb = clampHDRRange(diff.rgb);
|
||||||
frag_color = diff;
|
frag_color = diff;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,8 @@ vec3 legacyGamma(vec3 color)
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vec3 clampHDRRange(vec3 color);
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
//this is the one of the rare spots where diffuseRect contains linear color values (not sRGB)
|
//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);
|
diff.rgb = legacyGamma(diff.rgb);
|
||||||
#endif
|
#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()
|
void main()
|
||||||
|
|
@ -84,6 +85,7 @@ void main()
|
||||||
diff.rgb += nz*0.003;
|
diff.rgb += nz*0.003;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
diff.rgb = clampHDRRange(diff.rgb);
|
||||||
frag_color = diff;
|
frag_color = diff;
|
||||||
|
|
||||||
gl_FragDepth = texture(depthMap, vary_fragcoord.xy).r;
|
gl_FragDepth = texture(depthMap, vary_fragcoord.xy).r;
|
||||||
|
|
|
||||||
|
|
@ -28,138 +28,13 @@
|
||||||
out vec4 frag_color;
|
out vec4 frag_color;
|
||||||
|
|
||||||
uniform sampler2D diffuseRect;
|
uniform sampler2D diffuseRect;
|
||||||
uniform sampler2D exposureMap;
|
|
||||||
|
|
||||||
uniform vec2 screen_res;
|
|
||||||
in vec2 vary_fragcoord;
|
in vec2 vary_fragcoord;
|
||||||
|
|
||||||
vec3 linear_to_srgb(vec3 cl);
|
vec3 linear_to_srgb(vec3 cl);
|
||||||
|
vec3 toneMap(vec3 color);
|
||||||
|
|
||||||
//===============================================================
|
vec3 clampHDRRange(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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
|
|
@ -172,6 +47,7 @@ void main()
|
||||||
diff.rgb = clamp(diff.rgb, vec3(0.0), vec3(1.0));
|
diff.rgb = clamp(diff.rgb, vec3(0.0), vec3(1.0));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
diff.rgb = clampHDRRange(diff.rgb);
|
||||||
//debugExposure(diff.rgb);
|
//debugExposure(diff.rgb);
|
||||||
frag_color = max(diff, vec4(0));
|
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);
|
vec3 atten);
|
||||||
|
|
||||||
GBufferInfo getGBuffer(vec2 screenpos);
|
GBufferInfo getGBuffer(vec2 screenpos);
|
||||||
|
vec3 clampHDRRange(vec3 color);
|
||||||
|
|
||||||
void adjustIrradiance(inout vec3 irradiance, float ambocc)
|
void adjustIrradiance(inout vec3 irradiance, float ambocc)
|
||||||
{
|
{
|
||||||
|
|
@ -278,6 +279,7 @@ void main()
|
||||||
float final_scale = 1;
|
float final_scale = 1;
|
||||||
if (classic_mode > 0)
|
if (classic_mode > 0)
|
||||||
final_scale = 1.1;
|
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;
|
frag_color.a = 0.0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -90,21 +90,15 @@ uniform sampler2D depthMap;
|
||||||
|
|
||||||
uniform sampler2D exclusionTex;
|
uniform sampler2D exclusionTex;
|
||||||
|
|
||||||
uniform float sunAngle;
|
uniform int classic_mode;
|
||||||
uniform float sunAngle2;
|
|
||||||
uniform vec3 lightDir;
|
uniform vec3 lightDir;
|
||||||
uniform vec3 specular;
|
uniform vec3 specular;
|
||||||
uniform float lightExp;
|
uniform float blurMultiplier;
|
||||||
uniform float refScale;
|
uniform float refScale;
|
||||||
uniform float kd;
|
uniform float kd;
|
||||||
uniform vec2 screenRes;
|
|
||||||
uniform vec3 normScale;
|
uniform vec3 normScale;
|
||||||
uniform float fresnelScale;
|
uniform float fresnelScale;
|
||||||
uniform float fresnelOffset;
|
uniform float fresnelOffset;
|
||||||
uniform float blurMultiplier;
|
|
||||||
uniform vec4 waterFogColor;
|
|
||||||
uniform vec3 waterFogColorLinear;
|
|
||||||
|
|
||||||
|
|
||||||
//bigWave is (refCoord.w, view.w);
|
//bigWave is (refCoord.w, view.w);
|
||||||
in vec4 refCoord;
|
in vec4 refCoord;
|
||||||
|
|
@ -126,6 +120,7 @@ vec3 linear_to_srgb(vec3 col);
|
||||||
|
|
||||||
vec3 atmosLighting(vec3 light);
|
vec3 atmosLighting(vec3 light);
|
||||||
vec3 scaleSoftClip(vec3 light);
|
vec3 scaleSoftClip(vec3 light);
|
||||||
|
vec3 toneMapNoExposure(vec3 color);
|
||||||
|
|
||||||
vec3 vN, vT, vB;
|
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 calculate the fresnel here.
|
||||||
// We do this by getting the dot product for each sets of waves, and applying scale and offset.
|
// 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, wave1),
|
||||||
dot(viewVec, (wave2 + wave3) * 0.5),
|
dot(viewVec, (wave2 + wave3) * 0.5),
|
||||||
dot(viewVec, wave3)
|
dot(viewVec, wave3)
|
||||||
) * fresnelScale + fresnelOffset;
|
) * fresnelScale + fresnelOffset);
|
||||||
|
|
||||||
df3 *= df3;
|
df3 *= df3;
|
||||||
|
|
||||||
df2 = vec2(
|
df2 = max(vec2(0), vec2(
|
||||||
df3.x + df3.y + df3.z,
|
df3.x + df3.y + df3.z,
|
||||||
dot(viewVec, wavef) * fresnelScale + fresnelOffset
|
dot(viewVec, wavef) * fresnelScale + fresnelOffset
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
|
|
@ -194,6 +189,7 @@ void main()
|
||||||
vB = cross(vN, vT);
|
vB = cross(vN, vT);
|
||||||
|
|
||||||
vec3 pos = vary_position.xyz;
|
vec3 pos = vary_position.xyz;
|
||||||
|
float linear_depth = 1 / -pos.z;
|
||||||
|
|
||||||
float dist = length(pos.xyz);
|
float dist = length(pos.xyz);
|
||||||
|
|
||||||
|
|
@ -216,6 +212,12 @@ void main()
|
||||||
vec3 df3 = vec3(0);
|
vec3 df3 = vec3(0);
|
||||||
vec2 df2 = vec2(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);
|
calculateFresnelFactors(df3, df2, normalize(view.xyz), wave1, wave2, wave3, wavef);
|
||||||
|
|
||||||
vec3 waver = wavef*3;
|
vec3 waver = wavef*3;
|
||||||
|
|
@ -230,7 +232,7 @@ void main()
|
||||||
vec3 norm = transform_normal(normalize(wavef));
|
vec3 norm = transform_normal(normalize(wavef));
|
||||||
|
|
||||||
vdu = clamp(vdu, 0, 1);
|
vdu = clamp(vdu, 0, 1);
|
||||||
wavef.z *= max(vdu*vdu*vdu, 0.1);
|
//wavef.z *= max(vdu*vdu*vdu, 0.1);
|
||||||
|
|
||||||
wavef = normalize(wavef);
|
wavef = normalize(wavef);
|
||||||
|
|
||||||
|
|
@ -245,11 +247,6 @@ void main()
|
||||||
|
|
||||||
distort2 = clamp(distort2, vec2(0), vec2(0.999));
|
distort2 = clamp(distort2, vec2(0), vec2(0.999));
|
||||||
|
|
||||||
vec3 sunlit;
|
|
||||||
vec3 amblit;
|
|
||||||
vec3 additive;
|
|
||||||
vec3 atten;
|
|
||||||
|
|
||||||
float shadow = 1.0f;
|
float shadow = 1.0f;
|
||||||
|
|
||||||
float water_mask = texture(exclusionTex, distort).r;
|
float water_mask = texture(exclusionTex, distort).r;
|
||||||
|
|
@ -258,17 +255,20 @@ void main()
|
||||||
shadow = sampleDirectionalShadow(pos.xyz, norm.xyz, distort);
|
shadow = sampleDirectionalShadow(pos.xyz, norm.xyz, distort);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
calcAtmosphericVarsLinear(pos.xyz, wavef, vary_light_dir, sunlit, amblit, additive, atten);
|
vec3 sunlit_linear = sunlit;
|
||||||
|
float fade = 1;
|
||||||
vec3 sunlit_linear = srgb_to_linear(sunlit);
|
|
||||||
float fade = 0;
|
|
||||||
#ifdef TRANSPARENT_WATER
|
#ifdef TRANSPARENT_WATER
|
||||||
float depth = texture(depthMap, distort).r;
|
float depth = texture(depthMap, distort).r;
|
||||||
|
|
||||||
vec3 refPos = getPositionWithNDC(vec3(distort*2.0-vec2(1.0), depth*2.0-1.0));
|
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".
|
// 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));
|
distort2 = mix(distort, distort2, min(1, fade * 10));
|
||||||
depth = texture(depthMap, distort2).r;
|
depth = texture(depthMap, distort2).r;
|
||||||
|
|
||||||
|
|
@ -289,8 +289,8 @@ void main()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
float metallic = 1.0;
|
float metallic = 1.0;
|
||||||
float perceptualRoughness = 0.1;
|
float perceptualRoughness = blurMultiplier;
|
||||||
float gloss = 0.95;
|
float gloss = 1 - perceptualRoughness;
|
||||||
|
|
||||||
vec3 irradiance = vec3(0);
|
vec3 irradiance = vec3(0);
|
||||||
vec3 radiance = vec3(0);
|
vec3 radiance = vec3(0);
|
||||||
|
|
@ -300,7 +300,7 @@ void main()
|
||||||
#ifdef WATER_MINIMAL
|
#ifdef WATER_MINIMAL
|
||||||
sampleReflectionProbesWater(irradiance, radiance, distort2, pos.xyz, wave_ibl.xyz, gloss, amblit);
|
sampleReflectionProbesWater(irradiance, radiance, distort2, pos.xyz, wave_ibl.xyz, gloss, amblit);
|
||||||
#elif WATER_MINIMAL_PLUS
|
#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
|
#endif
|
||||||
|
|
||||||
vec3 diffuseColor = vec3(0);
|
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);
|
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);
|
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.
|
// 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.
|
// 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.
|
// We shorten the fade here at the shoreline so it doesn't appear too soft from a distance.
|
||||||
fade *= 60;
|
fade *= 60;
|
||||||
fade = min(1, fade);
|
fade = min(1, fade);
|
||||||
color = mix(fb.rgb, color, 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)));
|
frag_color = min(vec4(1),max(vec4(color.rgb, spec * water_mask), vec4(0)));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,7 @@ RenderTonemapType 1 1
|
||||||
RenderTonemapMix 1 1
|
RenderTonemapMix 1 1
|
||||||
RenderDisableVintageMode 1 1
|
RenderDisableVintageMode 1 1
|
||||||
RenderMaxTextureResolution 1 2048
|
RenderMaxTextureResolution 1 2048
|
||||||
|
RenderReflectionProbeCount 1 256
|
||||||
|
|
||||||
//
|
//
|
||||||
// Low Graphics Settings
|
// Low Graphics Settings
|
||||||
|
|
@ -128,6 +129,7 @@ RenderTonemapType 1 1
|
||||||
RenderTonemapMix 1 0.7
|
RenderTonemapMix 1 0.7
|
||||||
RenderDisableVintageMode 1 0
|
RenderDisableVintageMode 1 0
|
||||||
RenderMaxTextureResolution 1 512
|
RenderMaxTextureResolution 1 512
|
||||||
|
RenderReflectionProbeCount 1 1
|
||||||
|
|
||||||
//
|
//
|
||||||
// Medium Low Graphics Settings
|
// Medium Low Graphics Settings
|
||||||
|
|
@ -170,6 +172,7 @@ RenderTonemapType 1 1
|
||||||
RenderTonemapMix 1 0.7
|
RenderTonemapMix 1 0.7
|
||||||
RenderDisableVintageMode 1 0
|
RenderDisableVintageMode 1 0
|
||||||
RenderMaxTextureResolution 1 1024
|
RenderMaxTextureResolution 1 1024
|
||||||
|
RenderReflectionProbeCount 1 16
|
||||||
|
|
||||||
//
|
//
|
||||||
// Medium Graphics Settings (standard)
|
// Medium Graphics Settings (standard)
|
||||||
|
|
@ -211,6 +214,7 @@ RenderExposure 1 1
|
||||||
RenderTonemapType 1 1
|
RenderTonemapType 1 1
|
||||||
RenderTonemapMix 1 0.7
|
RenderTonemapMix 1 0.7
|
||||||
RenderMaxTextureResolution 1 2048
|
RenderMaxTextureResolution 1 2048
|
||||||
|
RenderReflectionProbeCount 1 32
|
||||||
|
|
||||||
//
|
//
|
||||||
// Medium High Graphics Settings
|
// Medium High Graphics Settings
|
||||||
|
|
@ -252,6 +256,7 @@ RenderExposure 1 1
|
||||||
RenderTonemapType 1 1
|
RenderTonemapType 1 1
|
||||||
RenderTonemapMix 1 0.7
|
RenderTonemapMix 1 0.7
|
||||||
RenderMaxTextureResolution 1 2048
|
RenderMaxTextureResolution 1 2048
|
||||||
|
RenderReflectionProbeCount 1 64
|
||||||
|
|
||||||
//
|
//
|
||||||
// High Graphics Settings (SSAO + sun shadows)
|
// High Graphics Settings (SSAO + sun shadows)
|
||||||
|
|
@ -293,6 +298,7 @@ RenderExposure 1 1
|
||||||
RenderTonemapType 1 1
|
RenderTonemapType 1 1
|
||||||
RenderTonemapMix 1 0.7
|
RenderTonemapMix 1 0.7
|
||||||
RenderMaxTextureResolution 1 2048
|
RenderMaxTextureResolution 1 2048
|
||||||
|
RenderReflectionProbeCount 1 128
|
||||||
|
|
||||||
//
|
//
|
||||||
// High Ultra Graphics Settings (deferred + SSAO + all shadows)
|
// High Ultra Graphics Settings (deferred + SSAO + all shadows)
|
||||||
|
|
@ -334,6 +340,7 @@ RenderExposure 1 1
|
||||||
RenderTonemapType 1 1
|
RenderTonemapType 1 1
|
||||||
RenderTonemapMix 1 0.7
|
RenderTonemapMix 1 0.7
|
||||||
RenderMaxTextureResolution 1 2048
|
RenderMaxTextureResolution 1 2048
|
||||||
|
RenderReflectionProbeCount 1 256
|
||||||
|
|
||||||
//
|
//
|
||||||
// Ultra graphics (REALLY PURTY!)
|
// Ultra graphics (REALLY PURTY!)
|
||||||
|
|
@ -375,6 +382,7 @@ RenderExposure 1 1
|
||||||
RenderTonemapType 1 1
|
RenderTonemapType 1 1
|
||||||
RenderTonemapMix 1 0.7
|
RenderTonemapMix 1 0.7
|
||||||
RenderMaxTextureResolution 1 2048
|
RenderMaxTextureResolution 1 2048
|
||||||
|
RenderReflectionProbeCount 1 256
|
||||||
|
|
||||||
//
|
//
|
||||||
// Class Unknown Hardware (unknown)
|
// Class Unknown Hardware (unknown)
|
||||||
|
|
@ -408,6 +416,7 @@ RenderReflectionProbeDetail 0 -1
|
||||||
RenderMirrors 0 0
|
RenderMirrors 0 0
|
||||||
RenderDisableVintageMode 1 0
|
RenderDisableVintageMode 1 0
|
||||||
RenderMaxTextureResolution 1 2048
|
RenderMaxTextureResolution 1 2048
|
||||||
|
RenderReflectionProbeCount 0 0
|
||||||
|
|
||||||
list Intel
|
list Intel
|
||||||
RenderAnisotropic 1 0
|
RenderAnisotropic 1 0
|
||||||
|
|
@ -429,6 +438,7 @@ RenderMirrors 0 0
|
||||||
RenderGLMultiThreadedTextures 0 0
|
RenderGLMultiThreadedTextures 0 0
|
||||||
RenderGLMultiThreadedMedia 0 0
|
RenderGLMultiThreadedMedia 0 0
|
||||||
RenderDisableVintageMode 1 0
|
RenderDisableVintageMode 1 0
|
||||||
|
RenderReflectionProbeCount 0 0
|
||||||
|
|
||||||
list TexUnit16orLess
|
list TexUnit16orLess
|
||||||
RenderTerrainPBRDetail 1 -1
|
RenderTerrainPBRDetail 1 -1
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,7 @@ RenderTonemapMix 1 1
|
||||||
RenderDisableVintageMode 1 1
|
RenderDisableVintageMode 1 1
|
||||||
RenderDownScaleMethod 1 0
|
RenderDownScaleMethod 1 0
|
||||||
RenderMaxTextureResolution 1 2048
|
RenderMaxTextureResolution 1 2048
|
||||||
|
RenderReflectionProbeCount 1 256
|
||||||
|
|
||||||
//
|
//
|
||||||
// Low Graphics Settings
|
// Low Graphics Settings
|
||||||
|
|
@ -128,6 +129,7 @@ RenderTonemapType 1 1
|
||||||
RenderTonemapMix 1 0.7
|
RenderTonemapMix 1 0.7
|
||||||
RenderDisableVintageMode 1 0
|
RenderDisableVintageMode 1 0
|
||||||
RenderMaxTextureResolution 1 512
|
RenderMaxTextureResolution 1 512
|
||||||
|
RenderReflectionProbeCount 1 1
|
||||||
|
|
||||||
//
|
//
|
||||||
// Medium Low Graphics Settings
|
// Medium Low Graphics Settings
|
||||||
|
|
@ -170,6 +172,7 @@ RenderTonemapType 1 1
|
||||||
RenderTonemapMix 1 0.7
|
RenderTonemapMix 1 0.7
|
||||||
RenderDisableVintageMode 1 0
|
RenderDisableVintageMode 1 0
|
||||||
RenderMaxTextureResolution 1 1024
|
RenderMaxTextureResolution 1 1024
|
||||||
|
RenderReflectionProbeCount 1 16
|
||||||
|
|
||||||
//
|
//
|
||||||
// Medium Graphics Settings (standard)
|
// Medium Graphics Settings (standard)
|
||||||
|
|
@ -211,6 +214,7 @@ RenderExposure 1 1
|
||||||
RenderTonemapType 1 1
|
RenderTonemapType 1 1
|
||||||
RenderTonemapMix 1 0.7
|
RenderTonemapMix 1 0.7
|
||||||
RenderMaxTextureResolution 1 2048
|
RenderMaxTextureResolution 1 2048
|
||||||
|
RenderReflectionProbeCount 1 32
|
||||||
|
|
||||||
//
|
//
|
||||||
// Medium High Graphics Settings
|
// Medium High Graphics Settings
|
||||||
|
|
@ -252,6 +256,7 @@ RenderExposure 1 1
|
||||||
RenderTonemapType 1 1
|
RenderTonemapType 1 1
|
||||||
RenderTonemapMix 1 0.7
|
RenderTonemapMix 1 0.7
|
||||||
RenderMaxTextureResolution 1 2048
|
RenderMaxTextureResolution 1 2048
|
||||||
|
RenderReflectionProbeCount 1 64
|
||||||
|
|
||||||
//
|
//
|
||||||
// High Graphics Settings (SSAO + sun shadows)
|
// High Graphics Settings (SSAO + sun shadows)
|
||||||
|
|
@ -293,6 +298,7 @@ RenderExposure 1 1
|
||||||
RenderTonemapType 1 1
|
RenderTonemapType 1 1
|
||||||
RenderTonemapMix 1 0.7
|
RenderTonemapMix 1 0.7
|
||||||
RenderMaxTextureResolution 1 2048
|
RenderMaxTextureResolution 1 2048
|
||||||
|
RenderReflectionProbeCount 1 128
|
||||||
|
|
||||||
//
|
//
|
||||||
// High Ultra Graphics Settings (SSAO + all shadows)
|
// High Ultra Graphics Settings (SSAO + all shadows)
|
||||||
|
|
@ -334,6 +340,7 @@ RenderExposure 1 1
|
||||||
RenderTonemapType 1 1
|
RenderTonemapType 1 1
|
||||||
RenderTonemapMix 1 0.7
|
RenderTonemapMix 1 0.7
|
||||||
RenderMaxTextureResolution 1 2048
|
RenderMaxTextureResolution 1 2048
|
||||||
|
RenderReflectionProbeCount 1 256
|
||||||
|
|
||||||
//
|
//
|
||||||
// Ultra graphics (REALLY PURTY!)
|
// Ultra graphics (REALLY PURTY!)
|
||||||
|
|
@ -375,6 +382,7 @@ RenderExposure 1 1
|
||||||
RenderTonemapType 1 1
|
RenderTonemapType 1 1
|
||||||
RenderTonemapMix 1 0.7
|
RenderTonemapMix 1 0.7
|
||||||
RenderMaxTextureResolution 1 2048
|
RenderMaxTextureResolution 1 2048
|
||||||
|
RenderReflectionProbeCount 1 256
|
||||||
|
|
||||||
//
|
//
|
||||||
// Class Unknown Hardware (unknown)
|
// Class Unknown Hardware (unknown)
|
||||||
|
|
@ -407,6 +415,7 @@ RenderShadowDetail 0 0
|
||||||
RenderMirrors 0 0
|
RenderMirrors 0 0
|
||||||
RenderDisableVintageMode 1 0
|
RenderDisableVintageMode 1 0
|
||||||
RenderMaxTextureResolution 1 2048
|
RenderMaxTextureResolution 1 2048
|
||||||
|
RenderReflectionProbeCount 0 0
|
||||||
|
|
||||||
list TexUnit8orLess
|
list TexUnit8orLess
|
||||||
RenderDeferredSSAO 0 0
|
RenderDeferredSSAO 0 0
|
||||||
|
|
@ -447,6 +456,7 @@ RenderReflectionProbeDetail 0 0
|
||||||
RenderReflectionsEnabled 0 0
|
RenderReflectionsEnabled 0 0
|
||||||
RenderMirrors 0 0
|
RenderMirrors 0 0
|
||||||
RenderDisableVintageMode 1 0
|
RenderDisableVintageMode 1 0
|
||||||
|
RenderReflectionProbeCount 0 0
|
||||||
|
|
||||||
list VaryingVectors16orLess
|
list VaryingVectors16orLess
|
||||||
RenderTerrainPBRPlanarSampleCount 1 1
|
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().getSculptID() == gltf_id);
|
||||||
llassert(obj->getVolume()->getParams().getSculptType() == LL_SCULPT_TYPE_GLTF);
|
llassert(obj->getVolume()->getParams().getSculptType() == LL_SCULPT_TYPE_GLTF);
|
||||||
|
|
||||||
if (obj->mGLTFAsset)
|
if (obj->mGLTFAsset || obj->mIsGLTFAssetMissing )
|
||||||
{ // object already has a GLTF asset, don't reload it
|
{
|
||||||
|
// 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
|
// 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());
|
// llassert(std::find(mObjects.begin(), mObjects.end(), obj) != mObjects.end());
|
||||||
|
|
@ -398,16 +399,19 @@ void GLTFSceneManager::onGLTFBinLoadComplete(const LLUUID& id, LLAssetType::ETyp
|
||||||
}
|
}
|
||||||
else
|
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->mGLTFAsset = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
obj->unref(); // todo: use LLPointer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
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();
|
obj->unref();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -446,7 +450,8 @@ void GLTFSceneManager::onGLTFLoadComplete(const LLUUID& id, LLAssetType::EType a
|
||||||
}
|
}
|
||||||
else
|
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();
|
obj->unref();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -455,7 +460,8 @@ void GLTFSceneManager::onGLTFLoadComplete(const LLUUID& id, LLAssetType::EType a
|
||||||
}
|
}
|
||||||
else
|
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();
|
obj->unref();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -517,6 +523,7 @@ void GLTFSceneManager::update()
|
||||||
if (mUploadingObject)
|
if (mUploadingObject)
|
||||||
{
|
{
|
||||||
mUploadingObject->mGLTFAsset = nullptr;
|
mUploadingObject->mGLTFAsset = nullptr;
|
||||||
|
mUploadingObject->mIsGLTFAssetMissing = false;
|
||||||
mUploadingObject->setGLTFAsset(assetId);
|
mUploadingObject->setGLTFAsset(assetId);
|
||||||
mUploadingObject->markForUpdate();
|
mUploadingObject->markForUpdate();
|
||||||
mUploadingObject = nullptr;
|
mUploadingObject = nullptr;
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@
|
||||||
#include "llgroupmgr.h"
|
#include "llgroupmgr.h"
|
||||||
#include "llhudmanager.h"
|
#include "llhudmanager.h"
|
||||||
#include "lljoystickbutton.h"
|
#include "lljoystickbutton.h"
|
||||||
|
#include "lllandmarkactions.h"
|
||||||
#include "llmorphview.h"
|
#include "llmorphview.h"
|
||||||
#include "llmoveview.h"
|
#include "llmoveview.h"
|
||||||
#include "llnavigationbar.h" // to show/hide navigation bar when changing mouse look state
|
#include "llnavigationbar.h" // to show/hide navigation bar when changing mouse look state
|
||||||
|
|
@ -2501,7 +2502,10 @@ void LLAgent::endAnimationUpdateUI()
|
||||||
gAgentAvatarp->updateAttachmentVisibility(gAgentCamera.getCameraMode());
|
gAgentAvatarp->updateAttachmentVisibility(gAgentCamera.getCameraMode());
|
||||||
}
|
}
|
||||||
|
|
||||||
gFloaterTools->dirty();
|
if (gFloaterTools)
|
||||||
|
{
|
||||||
|
gFloaterTools->dirty();
|
||||||
|
}
|
||||||
|
|
||||||
// Don't let this be called more than once if the camera
|
// Don't let this be called more than once if the camera
|
||||||
// mode hasn't changed. --JC
|
// 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)
|
void LLAgent::doTeleportViaLandmark(const LLUUID& landmark_asset_id)
|
||||||
{
|
{
|
||||||
LLViewerRegion *regionp = getRegion();
|
LLViewerRegion* regionp = getRegion();
|
||||||
if(regionp && teleportCore())
|
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()
|
LL_INFOS("Teleport") << "Sending TeleportLandmarkRequest. Current region handle " << regionp->getHandle()
|
||||||
<< " region id " << regionp->getRegionID()
|
<< " region id " << regionp->getRegionID()
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
#include "llagentpicksinfo.h"
|
#include "llagentpicksinfo.h"
|
||||||
|
|
||||||
#include "llagent.h"
|
#include "llagent.h"
|
||||||
|
#include "llagentbenefits.h"
|
||||||
#include "llavatarpropertiesprocessor.h"
|
#include "llavatarpropertiesprocessor.h"
|
||||||
|
|
||||||
const S32 MAX_AVATAR_PICKS = 10;
|
const S32 MAX_AVATAR_PICKS = 10;
|
||||||
|
|
@ -85,10 +86,9 @@ private:
|
||||||
|
|
||||||
LLAgentPicksInfo::LLAgentPicksInfo()
|
LLAgentPicksInfo::LLAgentPicksInfo()
|
||||||
: mAgentPicksObserver(NULL)
|
: mAgentPicksObserver(NULL)
|
||||||
, mMaxNumberOfPicks(MAX_AVATAR_PICKS)
|
|
||||||
// Disable Pick creation until we get number of Picks from server - in case
|
// Disable Pick creation until we get number of Picks from server - in case
|
||||||
// avatar has maximum number of Picks.
|
// avatar has maximum number of Picks.
|
||||||
, mNumberOfPicks(mMaxNumberOfPicks)
|
, mNumberOfPicks(S32_MAX)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -110,7 +110,13 @@ void LLAgentPicksInfo::requestNumberOfPicks()
|
||||||
mAgentPicksObserver->sendAgentPicksRequest();
|
mAgentPicksObserver->sendAgentPicksRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LLAgentPicksInfo::isPickLimitReached()
|
// static
|
||||||
|
S32 LLAgentPicksInfo::getMaxNumberOfPicks()
|
||||||
|
{
|
||||||
|
return LLAgentBenefitsMgr::current().getPicksLimit();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LLAgentPicksInfo::isPickLimitReached() const
|
||||||
{
|
{
|
||||||
return getNumberOfPicks() >= getMaxNumberOfPicks();
|
return getNumberOfPicks() >= getMaxNumberOfPicks();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,17 +52,17 @@ public:
|
||||||
/**
|
/**
|
||||||
* Returns number of Picks.
|
* Returns number of Picks.
|
||||||
*/
|
*/
|
||||||
S32 getNumberOfPicks() { return mNumberOfPicks; }
|
S32 getNumberOfPicks() const { return mNumberOfPicks; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns maximum number of Picks.
|
* Returns maximum number of Picks.
|
||||||
*/
|
*/
|
||||||
S32 getMaxNumberOfPicks() { return mMaxNumberOfPicks; }
|
static S32 getMaxNumberOfPicks();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if Agent has maximum allowed number of Picks.
|
* 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
|
* 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; }
|
void setNumberOfPicks(S32 number) { mNumberOfPicks = number; }
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets maximum number of Picks.
|
|
||||||
*/
|
|
||||||
void setMaxNumberOfPicks(S32 max_picks) { mMaxNumberOfPicks = max_picks; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
LLAgentPicksObserver* mAgentPicksObserver;
|
LLAgentPicksObserver* mAgentPicksObserver;
|
||||||
S32 mMaxNumberOfPicks;
|
|
||||||
S32 mNumberOfPicks;
|
S32 mNumberOfPicks;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1385,8 +1385,6 @@ void AISUpdate::parseCategory(const LLSD& category_map, S32 depth)
|
||||||
&& curr_cat->getVersion() > LLViewerInventoryCategory::VERSION_UNKNOWN
|
&& curr_cat->getVersion() > LLViewerInventoryCategory::VERSION_UNKNOWN
|
||||||
&& version > curr_cat->getVersion())
|
&& 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
|
LL_DEBUGS("Inventory") << "Category " << category_id
|
||||||
<< " is stale. Known version: " << curr_cat->getVersion()
|
<< " is stale. Known version: " << curr_cat->getVersion()
|
||||||
<< " server version: " << version << LL_ENDL;
|
<< " server version: " << version << LL_ENDL;
|
||||||
|
|
|
||||||
|
|
@ -301,7 +301,8 @@ struct AttachmentInfo
|
||||||
AttachmentInfo(metadata.logFilePathname, "text/plain"),
|
AttachmentInfo(metadata.logFilePathname, "text/plain"),
|
||||||
AttachmentInfo(metadata.userSettingsPathname, "text/xml"),
|
AttachmentInfo(metadata.userSettingsPathname, "text/xml"),
|
||||||
AttachmentInfo(metadata.accountSettingsPathname, "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;
|
secondLogPath = metadata.secondLogFilePathname;
|
||||||
|
|
|
||||||
|
|
@ -349,10 +349,6 @@ std::string gLastVersionChannel;
|
||||||
LLVector3 gWindVec(3.0, 3.0, 0.0);
|
LLVector3 gWindVec(3.0, 3.0, 0.0);
|
||||||
LLVector3 gRelativeWindVec(0.0, 0.0, 0.0);
|
LLVector3 gRelativeWindVec(0.0, 0.0, 0.0);
|
||||||
|
|
||||||
U32 gPacketsIn = 0;
|
|
||||||
|
|
||||||
bool gPrintMessagesThisFrame = false;
|
|
||||||
|
|
||||||
bool gRandomizeFramerate = false;
|
bool gRandomizeFramerate = false;
|
||||||
bool gPeriodicSlowFrame = false;
|
bool gPeriodicSlowFrame = false;
|
||||||
|
|
||||||
|
|
@ -361,6 +357,7 @@ bool gLLErrorActivated = false;
|
||||||
bool gLogoutInProgress = false;
|
bool gLogoutInProgress = false;
|
||||||
|
|
||||||
bool gSimulateMemLeak = false;
|
bool gSimulateMemLeak = false;
|
||||||
|
bool gDoDisconnect = false;
|
||||||
|
|
||||||
// We don't want anyone, especially threads working on the graphics pipeline,
|
// We don't want anyone, especially threads working on the graphics pipeline,
|
||||||
// to have to block due to this WorkQueue being full.
|
// 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 START_MARKER_FILE_NAME("SecondLife.start_marker");
|
||||||
const std::string ERROR_MARKER_FILE_NAME("SecondLife.error_marker");
|
const std::string ERROR_MARKER_FILE_NAME("SecondLife.error_marker");
|
||||||
const std::string LOGOUT_MARKER_FILE_NAME("SecondLife.logout_marker");
|
const std::string LOGOUT_MARKER_FILE_NAME("SecondLife.logout_marker");
|
||||||
static bool gDoDisconnect = false;
|
|
||||||
static std::string gLaunchFileOnQuit;
|
static std::string gLaunchFileOnQuit;
|
||||||
|
|
||||||
// Used on Win32 for other apps to identify our window (eg, win_setup)
|
// 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");
|
LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df pauseMainloopTimeout");
|
||||||
pingMainloopTimeout("Main:Sleep");
|
pingMainloopTimeout("Main:Sleep");
|
||||||
|
|
||||||
pauseMainloopTimeout();
|
pauseMainloopTimeout();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sleep and run background threads
|
// Sleep and run background threads
|
||||||
|
|
@ -2359,6 +2355,14 @@ void LLAppViewer::initLoggingAndGetLastDuration()
|
||||||
{
|
{
|
||||||
LL_WARNS("MarkerFile") << duration_log_msg << LL_ENDL;
|
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);
|
std::string strings_path_full = gDirUtilp->findSkinnedFilenameBaseLang(LLDir::XUI, strings_file);
|
||||||
if (strings_path_full.empty() || !LLFile::isfile(strings_path_full))
|
if (strings_path_full.empty() || !LLFile::isfile(strings_path_full))
|
||||||
{
|
{
|
||||||
|
std::string crash_reason;
|
||||||
if (strings_path_full.empty())
|
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
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -2996,24 +3001,23 @@ void LLAppViewer::initStrings()
|
||||||
int rc = LLFile::stat(strings_path_full, &st);
|
int rc = LLFile::stat(strings_path_full, &st);
|
||||||
if (rc != 0)
|
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))
|
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
|
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
|
// initial check to make sure files are there failed
|
||||||
gDirUtilp->dumpCurrentDirectories(LLError::LEVEL_WARN);
|
gDirUtilp->dumpCurrentDirectories(LLError::LEVEL_WARN);
|
||||||
LLError::LLUserWarningMsg::showMissingFiles();
|
LLError::LLUserWarningMsg::showMissingFiles();
|
||||||
LL_ERRS() << "Viewer failed to find localization and UI files."
|
LL_ERRS() << "Viewer failed to open some of localization and UI files."
|
||||||
<< " Please reinstall viewer from https://secondlife.com/support/downloads"
|
<< " " << crash_reason << "." << LL_ENDL;
|
||||||
<< " and contact https://support.secondlife.com if issue persists after reinstall." << LL_ENDL;
|
|
||||||
}
|
}
|
||||||
LLTransUtil::parseStrings(strings_file, default_trans_args);
|
LLTransUtil::parseStrings(strings_file, default_trans_args);
|
||||||
LLTransUtil::parseLanguageStrings("language_settings.xml");
|
LLTransUtil::parseLanguageStrings("language_settings.xml");
|
||||||
|
|
@ -4254,7 +4258,7 @@ U32 LLAppViewer::getTextureCacheVersion()
|
||||||
U32 LLAppViewer::getDiskCacheVersion()
|
U32 LLAppViewer::getDiskCacheVersion()
|
||||||
{
|
{
|
||||||
// Viewer disk cache version intorduced in Simple Cache Viewer, change if the cache format changes.
|
// 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 ;
|
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()
|
void LLAppViewer::loadNameCache()
|
||||||
{
|
{
|
||||||
// display names cache
|
// display names cache
|
||||||
std::string filename =
|
std::string filename = get_name_cache_filename("avatar_name_cache", "xml");
|
||||||
gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "avatar_name_cache.xml");
|
|
||||||
LL_INFOS("AvNameCache") << filename << LL_ENDL;
|
LL_INFOS("AvNameCache") << filename << LL_ENDL;
|
||||||
llifstream name_cache_stream(filename.c_str());
|
llifstream name_cache_stream(filename.c_str());
|
||||||
if(name_cache_stream.is_open())
|
if(name_cache_stream.is_open())
|
||||||
|
|
@ -4592,8 +4617,8 @@ void LLAppViewer::loadNameCache()
|
||||||
|
|
||||||
if (!gCacheName) return;
|
if (!gCacheName) return;
|
||||||
|
|
||||||
std::string name_cache;
|
// is there a reason for the "cache" extention?
|
||||||
name_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "name.cache");
|
std::string name_cache = get_name_cache_filename("name", "cache");
|
||||||
llifstream cache_file(name_cache.c_str());
|
llifstream cache_file(name_cache.c_str());
|
||||||
if(cache_file.is_open())
|
if(cache_file.is_open())
|
||||||
{
|
{
|
||||||
|
|
@ -4604,8 +4629,7 @@ void LLAppViewer::loadNameCache()
|
||||||
void LLAppViewer::saveNameCache()
|
void LLAppViewer::saveNameCache()
|
||||||
{
|
{
|
||||||
// display names cache
|
// display names cache
|
||||||
std::string filename =
|
std::string filename = get_name_cache_filename("avatar_name_cache", "xml");
|
||||||
gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "avatar_name_cache.xml");
|
|
||||||
llofstream name_cache_stream(filename.c_str());
|
llofstream name_cache_stream(filename.c_str());
|
||||||
if(name_cache_stream.is_open())
|
if(name_cache_stream.is_open())
|
||||||
{
|
{
|
||||||
|
|
@ -4615,8 +4639,7 @@ void LLAppViewer::saveNameCache()
|
||||||
// real names cache
|
// real names cache
|
||||||
if (gCacheName)
|
if (gCacheName)
|
||||||
{
|
{
|
||||||
std::string name_cache;
|
std::string name_cache = get_name_cache_filename("name", "cache");
|
||||||
name_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "name.cache");
|
|
||||||
llofstream cache_file(name_cache.c_str());
|
llofstream cache_file(name_cache.c_str());
|
||||||
if(cache_file.is_open())
|
if(cache_file.is_open())
|
||||||
{
|
{
|
||||||
|
|
@ -4894,6 +4917,20 @@ void LLAppViewer::idle()
|
||||||
|
|
||||||
if (gTeleportDisplay)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -5330,12 +5367,9 @@ void LLAppViewer::idleNameCache()
|
||||||
// Handle messages, and all message related stuff
|
// Handle messages, and all message related stuff
|
||||||
//
|
//
|
||||||
|
|
||||||
#define TIME_THROTTLE_MESSAGES
|
|
||||||
|
|
||||||
#ifdef TIME_THROTTLE_MESSAGES
|
constexpr F32 CHECK_MESSAGES_DEFAULT_MAX_TIME = 0.020f; // 50 ms = 50 fps (just for messages!)
|
||||||
#define CHECK_MESSAGES_DEFAULT_MAX_TIME .020f // 50 ms = 50 fps (just for messages!)
|
|
||||||
static F32 CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME;
|
static F32 CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME;
|
||||||
#endif
|
|
||||||
|
|
||||||
static LLTrace::BlockTimerStatHandle FTM_IDLE_NETWORK("Idle Network");
|
static LLTrace::BlockTimerStatHandle FTM_IDLE_NETWORK("Idle Network");
|
||||||
static LLTrace::BlockTimerStatHandle FTM_MESSAGE_ACKS("Message Acks");
|
static LLTrace::BlockTimerStatHandle FTM_MESSAGE_ACKS("Message Acks");
|
||||||
|
|
@ -5362,6 +5396,7 @@ void LLAppViewer::idleNetwork()
|
||||||
F32 total_time = 0.0f;
|
F32 total_time = 0.0f;
|
||||||
|
|
||||||
{
|
{
|
||||||
|
bool needs_drain = false;
|
||||||
LockMessageChecker lmc(gMessageSystem);
|
LockMessageChecker lmc(gMessageSystem);
|
||||||
while (lmc.checkAllMessages(frame_count, gServicePump))
|
while (lmc.checkAllMessages(frame_count, gServicePump))
|
||||||
{
|
{
|
||||||
|
|
@ -5374,54 +5409,44 @@ void LLAppViewer::idleNetwork()
|
||||||
}
|
}
|
||||||
|
|
||||||
total_decoded++;
|
total_decoded++;
|
||||||
gPacketsIn++;
|
|
||||||
|
|
||||||
if (total_decoded > MESSAGE_MAX_PER_FRAME)
|
if (total_decoded > MESSAGE_MAX_PER_FRAME)
|
||||||
{
|
{
|
||||||
|
needs_drain = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef TIME_THROTTLE_MESSAGES
|
|
||||||
// Prevent slow packets from completely destroying the frame rate.
|
// Prevent slow packets from completely destroying the frame rate.
|
||||||
// This usually happens due to clumps of avatars taking huge amount
|
// This usually happens due to clumps of avatars taking huge amount
|
||||||
// of network processing time (which needs to be fixed, but this is
|
// of network processing time (which needs to be fixed, but this is
|
||||||
// a good limit anyway).
|
// a good limit anyway).
|
||||||
total_time = check_message_timer.getElapsedTimeF32();
|
total_time = check_message_timer.getElapsedTimeF32();
|
||||||
if (total_time >= CheckMessagesMaxTime)
|
if (total_time >= CheckMessagesMaxTime)
|
||||||
|
{
|
||||||
|
needs_drain = true;
|
||||||
break;
|
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.
|
// Handle per-frame message system processing.
|
||||||
lmc.processAcks(gSavedSettings.getF32("AckCollectTime"));
|
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);
|
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"); });
|
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()
|
void LLAppViewer::forceErrorThreadCrash()
|
||||||
{
|
{
|
||||||
class LLCrashTestThread : public LLThread
|
class LLCrashTestThread : public LLThread
|
||||||
|
|
|
||||||
|
|
@ -157,9 +157,6 @@ public:
|
||||||
void loadNameCache();
|
void loadNameCache();
|
||||||
void saveNameCache();
|
void saveNameCache();
|
||||||
|
|
||||||
void loadExperienceCache();
|
|
||||||
void saveExperienceCache();
|
|
||||||
|
|
||||||
void removeMarkerFiles();
|
void removeMarkerFiles();
|
||||||
void recordSessionToMarker();
|
void recordSessionToMarker();
|
||||||
|
|
||||||
|
|
@ -175,6 +172,8 @@ public:
|
||||||
virtual void forceErrorOSSpecificException();
|
virtual void forceErrorOSSpecificException();
|
||||||
virtual void forceErrorDriverCrash();
|
virtual void forceErrorDriverCrash();
|
||||||
virtual void forceErrorCoroutineCrash();
|
virtual void forceErrorCoroutineCrash();
|
||||||
|
virtual void forceErrorCoroprocedureCrash();
|
||||||
|
virtual void forceErrorWorkQueueCrash();
|
||||||
virtual void forceErrorThreadCrash();
|
virtual void forceErrorThreadCrash();
|
||||||
|
|
||||||
// The list is found in app_settings/settings_files.xml
|
// The list is found in app_settings/settings_files.xml
|
||||||
|
|
@ -411,11 +410,10 @@ extern std::string gLastVersionChannel;
|
||||||
|
|
||||||
extern LLVector3 gWindVec;
|
extern LLVector3 gWindVec;
|
||||||
extern LLVector3 gRelativeWindVec;
|
extern LLVector3 gRelativeWindVec;
|
||||||
extern U32 gPacketsIn;
|
|
||||||
extern bool gPrintMessagesThisFrame;
|
|
||||||
|
|
||||||
extern bool gRandomizeFramerate;
|
extern bool gRandomizeFramerate;
|
||||||
extern bool gPeriodicSlowFrame;
|
extern bool gPeriodicSlowFrame;
|
||||||
|
extern bool gDoDisconnect;
|
||||||
|
|
||||||
extern bool gSimulateMemLeak;
|
extern bool gSimulateMemLeak;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ void clearDumpLogsDir();
|
||||||
struct CrashMetadata
|
struct CrashMetadata
|
||||||
{
|
{
|
||||||
std::string logFilePathname;
|
std::string logFilePathname;
|
||||||
|
std::string attributesPathname;
|
||||||
std::string userSettingsPathname;
|
std::string userSettingsPathname;
|
||||||
std::string accountSettingsPathname;
|
std::string accountSettingsPathname;
|
||||||
std::string staticDebugPathname;
|
std::string staticDebugPathname;
|
||||||
|
|
|
||||||
|
|
@ -172,6 +172,8 @@ CrashMetadataSingleton::CrashMetadataSingleton()
|
||||||
// Note: we depend on being able to read the static_debug_info.log file
|
// 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
|
// from the *previous* run before we overwrite it with the new one for
|
||||||
// *this* run. LLAppViewer initialization must happen in the Right Order.
|
// *this* run. LLAppViewer initialization must happen in the Right Order.
|
||||||
|
|
||||||
|
// Todo: consider converting static file into bugspalt attributes file
|
||||||
staticDebugPathname = *gViewerAppPtr->getStaticDebugFile();
|
staticDebugPathname = *gViewerAppPtr->getStaticDebugFile();
|
||||||
std::ifstream static_file(staticDebugPathname);
|
std::ifstream static_file(staticDebugPathname);
|
||||||
LLSD info;
|
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++.
|
// Avoid having to compile all of our LLSingleton machinery in Objective-C++.
|
||||||
|
|
|
||||||
|
|
@ -804,14 +804,16 @@ bool LLAppViewerWin32::cleanup()
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLAppViewerWin32::reportCrashToBugsplat(void* pExcepInfo)
|
bool LLAppViewerWin32::reportCrashToBugsplat(void* pExcepInfo)
|
||||||
{
|
{
|
||||||
#if defined(LL_BUGSPLAT)
|
#if defined(LL_BUGSPLAT)
|
||||||
if (sBugSplatSender)
|
if (sBugSplatSender)
|
||||||
{
|
{
|
||||||
sBugSplatSender->createReport((EXCEPTION_POINTERS*)pExcepInfo);
|
sBugSplatSender->createReport((EXCEPTION_POINTERS*)pExcepInfo);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
#endif // LL_BUGSPLAT
|
#endif // LL_BUGSPLAT
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLAppViewerWin32::initLoggingAndGetLastDuration()
|
void LLAppViewerWin32::initLoggingAndGetLastDuration()
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ public:
|
||||||
bool init() override; // Override to do application initialization
|
bool init() override; // Override to do application initialization
|
||||||
bool cleanup() override;
|
bool cleanup() override;
|
||||||
|
|
||||||
void reportCrashToBugsplat(void* pExcepInfo) override;
|
bool reportCrashToBugsplat(void* pExcepInfo) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void initLoggingAndGetLastDuration() override; // Override to clean stack_trace info.
|
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)
|
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
|
// Must ask for a pick based on the creator id because
|
||||||
// the pick database is distributed to the inventory cluster. JC
|
// the pick database is distributed to the inventory cluster. JC
|
||||||
std::vector<std::string> request_params{ creator_id.asString(), pick_id.asString() };
|
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 LLNotificationChiclet::ChicletNotificationChannel::filterNotification( LLNotificationPtr notification )
|
||||||
{
|
{
|
||||||
bool displayNotification;
|
bool displayNotification;
|
||||||
|
LLFloaterNotificationsTabbed* floater = LLFloaterNotificationsTabbed::getInstance();
|
||||||
if ( (notification->getName() == "ScriptDialog") // special case for scripts
|
if ( (notification->getName() == "ScriptDialog") // special case for scripts
|
||||||
// if there is no toast window for the notification, filter it
|
// if there is no toast window for the notification, filter it
|
||||||
//|| (!LLNotificationWellWindow::getInstance()->findItemByID(notification->getID()))
|
//|| (!LLNotificationWellWindow::getInstance()->findItemByID(notification->getID()))
|
||||||
|| (!LLFloaterNotificationsTabbed::getInstance()->findItemByID(notification->getID(), notification->getName()))
|
|| (floater && !floater->findItemByID(notification->getID(), notification->getName()))
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
displayNotification = false;
|
displayNotification = false;
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@
|
||||||
|
|
||||||
// viewer includes
|
// viewer includes
|
||||||
#include "llagent.h"
|
#include "llagent.h"
|
||||||
|
#include "llagentcamera.h"
|
||||||
#include "llcriticaldamp.h"
|
#include "llcriticaldamp.h"
|
||||||
#include "llface.h"
|
#include "llface.h"
|
||||||
#include "lllightconstants.h"
|
#include "lllightconstants.h"
|
||||||
|
|
@ -778,6 +779,14 @@ bool LLDrawable::updateMove()
|
||||||
|
|
||||||
makeActive();
|
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();
|
return isState(MOVE_UNDAMPED) ? updateMoveUndamped() : updateMoveDamped();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -176,154 +176,133 @@ void LLDrawPoolWater::renderPostDeferred(S32 pass)
|
||||||
light_diffuse *= (1.5f + (6.f * ground_proj_sq));
|
light_diffuse *= (1.5f + (6.f * ground_proj_sq));
|
||||||
}
|
}
|
||||||
|
|
||||||
// set up normal maps filtering
|
LLTexUnit::eTextureFilterOptions filter_mode = has_normal_mips ? LLTexUnit::TFO_ANISOTROPIC : LLTexUnit::TFO_POINT;
|
||||||
for (auto norm_map : mWaterNormp)
|
|
||||||
{
|
|
||||||
if (norm_map) norm_map->setFilteringOption(has_normal_mips ? LLTexUnit::TFO_ANISOTROPIC : LLTexUnit::TFO_POINT);
|
|
||||||
}
|
|
||||||
|
|
||||||
LLColor4 specular(sun_up ? psky->getSunlightColor() : psky->getMoonlightColor());
|
LLColor4 specular(sun_up ? psky->getSunlightColor() : psky->getMoonlightColor());
|
||||||
F32 phase_time = (F32) LLFrameTimer::getElapsedSeconds() * 0.5f;
|
F32 phase_time = (F32) LLFrameTimer::getElapsedSeconds() * 0.5f;
|
||||||
LLGLSLShader *shader = nullptr;
|
LLGLSLShader *shader = nullptr;
|
||||||
|
|
||||||
// two passes, first with standard water shader bound, second with edge water shader bound
|
// One pass, one of two shaders. Void water and region water share state.
|
||||||
for (int edge = 0; edge < 2; edge++)
|
// 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
|
shader = &gUnderWaterProgram;
|
||||||
if (underwater)
|
}
|
||||||
{
|
else
|
||||||
shader = &gUnderWaterProgram;
|
{
|
||||||
}
|
shader = &gWaterProgram;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gGL.getTexUnit(0)->activate();
|
gPipeline.bindDeferredShader(*shader, nullptr, &gPipeline.mWaterDis);
|
||||||
gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE);
|
|
||||||
|
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);
|
gGL.setColorMask(true, false);
|
||||||
}
|
}
|
||||||
|
|
@ -333,22 +312,18 @@ void LLDrawPoolWater::pushWaterPlanes(int pass)
|
||||||
LLVOWater* water = nullptr;
|
LLVOWater* water = nullptr;
|
||||||
for (LLFace* const& face : mDrawFace)
|
for (LLFace* const& face : mDrawFace)
|
||||||
{
|
{
|
||||||
if (!face)
|
|
||||||
continue;
|
|
||||||
water = static_cast<LLVOWater*>(face->getViewerObject());
|
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();
|
sNeedsReflectionUpdate = true;
|
||||||
|
sNeedsDistortionUpdate = true;
|
||||||
// Note non-void water being drawn, updates required
|
|
||||||
if (!pass) // SL-16461 remove !LLPipeline::sUseOcclusion check
|
|
||||||
{
|
|
||||||
sNeedsReflectionUpdate = true;
|
|
||||||
sNeedsDistortionUpdate = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -670,6 +670,7 @@ std::unique_ptr<std::vector<std::string>> LLFilePicker::navOpenFilterProc(ELoadF
|
||||||
break;
|
break;
|
||||||
case FFLOAD_HDRI:
|
case FFLOAD_HDRI:
|
||||||
allowedv->push_back("exr");
|
allowedv->push_back("exr");
|
||||||
|
case FFLOAD_MODEL:
|
||||||
case FFLOAD_COLLADA:
|
case FFLOAD_COLLADA:
|
||||||
allowedv->push_back("dae");
|
allowedv->push_back("dae");
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@
|
||||||
#include "llagentui.h"
|
#include "llagentui.h"
|
||||||
#include "llbase64.h"
|
#include "llbase64.h"
|
||||||
#include "llcallbacklist.h"
|
#include "llcallbacklist.h"
|
||||||
|
#include "lldate.h"
|
||||||
#include "llenvironment.h"
|
#include "llenvironment.h"
|
||||||
#include "llimagejpeg.h"
|
#include "llimagejpeg.h"
|
||||||
#include "llmediactrl.h"
|
#include "llmediactrl.h"
|
||||||
|
|
@ -692,7 +693,15 @@ void LLFloater360Capture::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent
|
||||||
);
|
);
|
||||||
|
|
||||||
// execute the command on the page
|
// 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;
|
break;
|
||||||
|
|
@ -773,7 +782,15 @@ void LLFloater360Capture::onSaveLocalBtn()
|
||||||
|
|
||||||
// send it to the browser instance, triggering the equirectangular capture
|
// send it to the browser instance, triggering the equirectangular capture
|
||||||
// process and complimentary offer to save the image
|
// 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
|
// 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 << "_";
|
filename << "_";
|
||||||
|
|
||||||
// add in the current HH-MM-SS (with leading 0's) so users can easily save many shots in same folder
|
// 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);
|
filename << LLDate::now().toLocalDateString("%Y%m%d_%H%M%S");
|
||||||
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;
|
|
||||||
|
|
||||||
// the unusual way we save the output image (originates in the
|
// the unusual way we save the output image (originates in the
|
||||||
// embedded browser and not the C++ code) means that the system
|
// embedded browser and not the C++ code) means that the system
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@
|
||||||
#include "llagent.h"
|
#include "llagent.h"
|
||||||
#include "llagentui.h"
|
#include "llagentui.h"
|
||||||
#include "llcombobox.h"
|
#include "llcombobox.h"
|
||||||
|
#include "llfloaterreg.h"
|
||||||
#include "llinventoryfunctions.h"
|
#include "llinventoryfunctions.h"
|
||||||
#include "llinventoryobserver.h"
|
#include "llinventoryobserver.h"
|
||||||
#include "lllandmarkactions.h"
|
#include "lllandmarkactions.h"
|
||||||
|
|
@ -389,6 +390,7 @@ void LLFloaterCreateLandmark::setItem(const uuid_set_t& items)
|
||||||
{
|
{
|
||||||
mItem = item;
|
mItem = item;
|
||||||
mAssetID = mItem->getAssetUUID();
|
mAssetID = mItem->getAssetUUID();
|
||||||
|
mParentID = mItem->getParentUUID();
|
||||||
setVisibleAndFrontmost(true);
|
setVisibleAndFrontmost(true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -418,8 +420,7 @@ void LLFloaterCreateLandmark::updateItem(const uuid_set_t& items, U32 mask)
|
||||||
closeFloater();
|
closeFloater();
|
||||||
}
|
}
|
||||||
|
|
||||||
LLUUID folder_id = mFolderCombo->getValue().asUUID();
|
if (mParentID != mItem->getParentUUID())
|
||||||
if (folder_id != mItem->getParentUUID())
|
|
||||||
{
|
{
|
||||||
// user moved landmark in inventory,
|
// user moved landmark in inventory,
|
||||||
// assume that we are done all other changes should already be commited
|
// assume that we are done all other changes should already be commited
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ private:
|
||||||
LLTextEditor* mNotesEditor;
|
LLTextEditor* mNotesEditor;
|
||||||
LLUUID mLandmarksID;
|
LLUUID mLandmarksID;
|
||||||
LLUUID mAssetID;
|
LLUUID mAssetID;
|
||||||
|
LLUUID mParentID;
|
||||||
|
|
||||||
LLLandmarksInventoryObserver* mInventoryObserver;
|
LLLandmarksInventoryObserver* mInventoryObserver;
|
||||||
LLPointer<LLInventoryItem> mItem;
|
LLPointer<LLInventoryItem> mItem;
|
||||||
|
|
|
||||||
|
|
@ -57,8 +57,7 @@ static const S32 USED_EMOJIS_IMAGE_INDEX = 0x23F2;
|
||||||
// https://www.compart.com/en/unicode/U+1F6D1
|
// https://www.compart.com/en/unicode/U+1F6D1
|
||||||
static const S32 EMPTY_LIST_IMAGE_INDEX = 0x1F6D1;
|
static const S32 EMPTY_LIST_IMAGE_INDEX = 0x1F6D1;
|
||||||
// The following categories should follow the required alphabetic order
|
// 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 = "frequently used";
|
||||||
static const std::string FREQUENTLY_USED_CATEGORY = "2 frequently used";
|
|
||||||
|
|
||||||
// Floater state related variables
|
// Floater state related variables
|
||||||
static std::list<llwchar> sRecentlyUsed;
|
static std::list<llwchar> sRecentlyUsed;
|
||||||
|
|
@ -445,11 +444,10 @@ void LLFloaterEmojiPicker::fillGroups()
|
||||||
params.name = "all_categories";
|
params.name = "all_categories";
|
||||||
createGroupButton(params, rect, ALL_EMOJIS_IMAGE_INDEX);
|
createGroupButton(params, rect, ALL_EMOJIS_IMAGE_INDEX);
|
||||||
|
|
||||||
// Create group and button for "Recently used" and/or "Frequently used"
|
// Create group and button for "Frequently used"
|
||||||
if (!sRecentlyUsed.empty() || !sFrequentlyUsed.empty())
|
if (!sFrequentlyUsed.empty())
|
||||||
{
|
{
|
||||||
std::map<std::string, std::vector<LLEmojiSearchResult>> cats;
|
std::map<std::string, std::vector<LLEmojiSearchResult>> cats;
|
||||||
fillCategoryRecentlyUsed(cats);
|
|
||||||
fillCategoryFrequentlyUsed(cats);
|
fillCategoryFrequentlyUsed(cats);
|
||||||
|
|
||||||
if (!cats.empty())
|
if (!cats.empty())
|
||||||
|
|
@ -482,40 +480,6 @@ void LLFloaterEmojiPicker::fillGroups()
|
||||||
resizeGroupButtons();
|
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)
|
void LLFloaterEmojiPicker::fillCategoryFrequentlyUsed(std::map<std::string, std::vector<LLEmojiSearchResult>>& cats)
|
||||||
{
|
{
|
||||||
if (sFrequentlyUsed.empty())
|
if (sFrequentlyUsed.empty())
|
||||||
|
|
@ -756,7 +720,6 @@ void LLFloaterEmojiPicker::fillEmojisCategory(const std::vector<LLEmojiSearchRes
|
||||||
{
|
{
|
||||||
// Place the category title
|
// Place the category title
|
||||||
std::string title =
|
std::string title =
|
||||||
category == RECENTLY_USED_CATEGORY ? getString("title_for_recently_used") :
|
|
||||||
category == FREQUENTLY_USED_CATEGORY ? getString("title_for_frequently_used") :
|
category == FREQUENTLY_USED_CATEGORY ? getString("title_for_frequently_used") :
|
||||||
isupper(category.front()) ? category : LLStringUtil::capitalize(category);
|
isupper(category.front()) ? category : LLStringUtil::capitalize(category);
|
||||||
LLEmojiGridDivider* div = new LLEmojiGridDivider(row_panel_params, title);
|
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();
|
const LLEmojiDictionary::emoji2descr_map_t& emoji2descr = LLEmojiDictionary::instance().getEmoji2Descr();
|
||||||
LLEmojiSearchResult emoji { 0, "", 0, 0 };
|
LLEmojiSearchResult emoji { 0, "", 0, 0 };
|
||||||
if (category == RECENTLY_USED_CATEGORY)
|
if (category == FREQUENTLY_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)
|
|
||||||
{
|
{
|
||||||
for (const auto& code : sFrequentlyUsed)
|
for (const auto& code : sFrequentlyUsed)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,6 @@ public:
|
||||||
private:
|
private:
|
||||||
void initialize();
|
void initialize();
|
||||||
void fillGroups();
|
void fillGroups();
|
||||||
void fillCategoryRecentlyUsed(std::map<std::string, std::vector<LLEmojiSearchResult>>& cats);
|
|
||||||
void fillCategoryFrequentlyUsed(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 fillGroupEmojis(std::map<std::string, std::vector<LLEmojiSearchResult>>& cats, U32 index);
|
||||||
void createGroupButton(LLButton::Params& params, const LLRect& rect, llwchar emoji);
|
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.
|
// the network handlers are de-synched from the rendering.
|
||||||
F32Milliseconds client_frame_time = frame_recording.getPeriodMean(LLStatViewer::FRAME_STACKTIME);
|
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)
|
if(packet_loss >= mNetworkPacketLossCritical)
|
||||||
{
|
{
|
||||||
mNetworkButton->setImageUnselected(LLUI::getUIImage(LAG_CRITICAL_IMAGE_NAME));
|
mNetworkButton->setImageUnselected(LLUI::getUIImage(LAG_CRITICAL_IMAGE_NAME));
|
||||||
|
|
|
||||||
|
|
@ -115,12 +115,12 @@ bool LLFloaterPerformance::postBuild()
|
||||||
mHUDList = mHUDsPanel->getChild<LLNameListCtrl>("hud_list");
|
mHUDList = mHUDsPanel->getChild<LLNameListCtrl>("hud_list");
|
||||||
mHUDList->setNameListType(LLNameListCtrl::SPECIAL);
|
mHUDList->setNameListType(LLNameListCtrl::SPECIAL);
|
||||||
mHUDList->setHoverIconName("StopReload_Off");
|
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 = mComplexityPanel->getChild<LLNameListCtrl>("obj_list");
|
||||||
mObjectList->setNameListType(LLNameListCtrl::SPECIAL);
|
mObjectList->setNameListType(LLNameListCtrl::SPECIAL);
|
||||||
mObjectList->setHoverIconName("StopReload_Off");
|
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>("advanced_btn")->setCommitCallback(boost::bind(&LLFloaterPerformance::onClickAdvanced, this));
|
||||||
mSettingsPanel->getChild<LLButton>("defaults_btn")->setCommitCallback(boost::bind(&LLFloaterPerformance::onClickDefaults, this));
|
mSettingsPanel->getChild<LLButton>("defaults_btn")->setCommitCallback(boost::bind(&LLFloaterPerformance::onClickDefaults, this));
|
||||||
|
|
@ -527,9 +527,13 @@ void LLFloaterPerformance::setFPSText()
|
||||||
mTextFPSLabel->setValue(fps_text);
|
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()
|
void LLFloaterPerformance::onClickAdvanced()
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ public:
|
||||||
void hidePanels();
|
void hidePanels();
|
||||||
void showAutoadjustmentsPanel();
|
void showAutoadjustmentsPanel();
|
||||||
|
|
||||||
void detachItem(const LLUUID& item_id);
|
void detachObject(const LLUUID& obj_id);
|
||||||
|
|
||||||
void onAvatarListRightClick(LLUICtrl* ctrl, S32 x, S32 y);
|
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.AutoAdjustments", boost::bind(&LLFloaterPreference::onClickAutoAdjustments, this));
|
||||||
mCommitCallbackRegistrar.add("Pref.HardwareDefaults", boost::bind(&LLFloaterPreference::setHardwareDefaults, this));
|
mCommitCallbackRegistrar.add("Pref.HardwareDefaults", boost::bind(&LLFloaterPreference::setHardwareDefaults, this));
|
||||||
mCommitCallbackRegistrar.add("Pref.AvatarImpostorsEnable", boost::bind(&LLFloaterPreference::onAvatarImpostorsEnable, 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.UpdateIndirectMaxComplexity", boost::bind(&LLFloaterPreference::updateMaxComplexity, this));
|
||||||
mCommitCallbackRegistrar.add("Pref.RenderOptionUpdate", boost::bind(&LLFloaterPreference::onRenderOptionEnable, this));
|
mCommitCallbackRegistrar.add("Pref.RenderOptionUpdate", boost::bind(&LLFloaterPreference::onRenderOptionEnable, this));
|
||||||
mCommitCallbackRegistrar.add("Pref.WindowedMod", boost::bind(&LLFloaterPreference::onCommitWindowedMode, 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 );
|
LLAvatarPropertiesProcessor::getInstance()->addObserver( gAgent.getID(), this );
|
||||||
|
|
||||||
mComplexityChangedSignal = gSavedSettings.getControl("RenderAvatarMaxComplexity")->getCommitSignal()->connect(boost::bind(&LLFloaterPreference::updateComplexityText, 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.ClearLog", boost::bind(&LLConversationLog::onClearLog, &LLConversationLog::instance()));
|
||||||
mCommitCallbackRegistrar.add("Pref.DeleteTranscripts", boost::bind(&LLFloaterPreference::onDeleteTranscripts, this));
|
mCommitCallbackRegistrar.add("Pref.DeleteTranscripts", boost::bind(&LLFloaterPreference::onDeleteTranscripts, this));
|
||||||
|
|
@ -542,7 +544,12 @@ void LLFloaterPreference::onDoNotDisturbResponseChanged()
|
||||||
LLFloaterPreference::~LLFloaterPreference()
|
LLFloaterPreference::~LLFloaterPreference()
|
||||||
{
|
{
|
||||||
LLConversationLog::instance().removeObserver(this);
|
LLConversationLog::instance().removeObserver(this);
|
||||||
|
if (LLAvatarPropertiesProcessor::instanceExists())
|
||||||
|
{
|
||||||
|
LLAvatarPropertiesProcessor::getInstance()->removeObserver(gAgent.getID(), this);
|
||||||
|
}
|
||||||
mComplexityChangedSignal.disconnect();
|
mComplexityChangedSignal.disconnect();
|
||||||
|
mImpostorsChangedSignal.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLFloaterPreference::draw()
|
void LLFloaterPreference::draw()
|
||||||
|
|
@ -1287,6 +1294,9 @@ void LLAvatarComplexityControls::setIndirectMaxArc()
|
||||||
void LLFloaterPreference::refresh()
|
void LLFloaterPreference::refresh()
|
||||||
{
|
{
|
||||||
LLPanel::refresh();
|
LLPanel::refresh();
|
||||||
|
setMaxNonImpostorsText(
|
||||||
|
gSavedSettings.getU32("RenderAvatarMaxNonImpostors"),
|
||||||
|
getChild<LLTextBox>("IndirectMaxNonImpostorsText", true));
|
||||||
LLAvatarComplexityControls::setText(
|
LLAvatarComplexityControls::setText(
|
||||||
gSavedSettings.getU32("RenderAvatarMaxComplexity"),
|
gSavedSettings.getU32("RenderAvatarMaxComplexity"),
|
||||||
getChild<LLTextBox>("IndirectMaxComplexityText", true));
|
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()
|
void LLFloaterPreference::updateMaxComplexity()
|
||||||
{
|
{
|
||||||
// Called when the IndirectMaxComplexity control changes
|
// Called when the IndirectMaxComplexity control changes
|
||||||
|
|
|
||||||
|
|
@ -206,6 +206,9 @@ private:
|
||||||
void onDeleteTranscripts();
|
void onDeleteTranscripts();
|
||||||
void onDeleteTranscriptsResponse(const LLSD& notification, const LLSD& response);
|
void onDeleteTranscriptsResponse(const LLSD& notification, const LLSD& response);
|
||||||
void updateDeleteTranscriptsButton();
|
void updateDeleteTranscriptsButton();
|
||||||
|
void updateMaxNonImpostors();
|
||||||
|
void updateIndirectMaxNonImpostors(const LLSD& newvalue);
|
||||||
|
void setMaxNonImpostorsText(U32 value, LLTextBox* text_box);
|
||||||
void updateMaxComplexity();
|
void updateMaxComplexity();
|
||||||
void updateComplexityText();
|
void updateComplexityText();
|
||||||
static bool loadFromFilename(const std::string& filename, std::map<std::string, std::string> &label_map);
|
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;
|
std::unique_ptr< ll::prefs::SearchData > mSearchData;
|
||||||
bool mSearchDataDirty;
|
bool mSearchDataDirty;
|
||||||
|
|
||||||
|
boost::signals2::connection mImpostorsChangedSignal;
|
||||||
boost::signals2::connection mComplexityChangedSignal;
|
boost::signals2::connection mComplexityChangedSignal;
|
||||||
|
|
||||||
void onUpdateFilterTerm( bool force = false );
|
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.Cancel", boost::bind(&LLFloaterPreferenceGraphicsAdvanced::onBtnCancel, this, _2));
|
||||||
mCommitCallbackRegistrar.add("Pref.OK", boost::bind(&LLFloaterPreferenceGraphicsAdvanced::onBtnOK, 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()
|
LLFloaterPreferenceGraphicsAdvanced::~LLFloaterPreferenceGraphicsAdvanced()
|
||||||
|
|
@ -58,7 +60,6 @@ LLFloaterPreferenceGraphicsAdvanced::~LLFloaterPreferenceGraphicsAdvanced()
|
||||||
mComplexityChangedSignal.disconnect();
|
mComplexityChangedSignal.disconnect();
|
||||||
mComplexityModeChangedSignal.disconnect();
|
mComplexityModeChangedSignal.disconnect();
|
||||||
mLODFactorChangedSignal.disconnect();
|
mLODFactorChangedSignal.disconnect();
|
||||||
mNumImpostorsChangedSignal.disconnect();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LLFloaterPreferenceGraphicsAdvanced::postBuild()
|
bool LLFloaterPreferenceGraphicsAdvanced::postBuild()
|
||||||
|
|
@ -254,8 +255,8 @@ void LLFloaterPreferenceGraphicsAdvanced::updateIndirectMaxNonImpostors(const LL
|
||||||
if ((value != 0) && (value != gSavedSettings.getU32("IndirectMaxNonImpostors")))
|
if ((value != 0) && (value != gSavedSettings.getU32("IndirectMaxNonImpostors")))
|
||||||
{
|
{
|
||||||
gSavedSettings.setU32("IndirectMaxNonImpostors", value);
|
gSavedSettings.setU32("IndirectMaxNonImpostors", value);
|
||||||
setMaxNonImpostorsText(value, getChild<LLTextBox>("IndirectMaxNonImpostorsText"));
|
|
||||||
}
|
}
|
||||||
|
setMaxNonImpostorsText(value, getChild<LLTextBox>("IndirectMaxNonImpostorsText"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLFloaterPreferenceGraphicsAdvanced::setMaxNonImpostorsText(U32 value, LLTextBox* text_box)
|
void LLFloaterPreferenceGraphicsAdvanced::setMaxNonImpostorsText(U32 value, LLTextBox* text_box)
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@ protected:
|
||||||
void onBtnOK(const LLSD& userdata);
|
void onBtnOK(const LLSD& userdata);
|
||||||
void onBtnCancel(const LLSD& userdata);
|
void onBtnCancel(const LLSD& userdata);
|
||||||
|
|
||||||
|
boost::signals2::connection mImpostorsChangedSignal;
|
||||||
boost::signals2::connection mComplexityChangedSignal;
|
boost::signals2::connection mComplexityChangedSignal;
|
||||||
boost::signals2::connection mComplexityModeChangedSignal;
|
boost::signals2::connection mComplexityModeChangedSignal;
|
||||||
boost::signals2::connection mLODFactorChangedSignal;
|
boost::signals2::connection mLODFactorChangedSignal;
|
||||||
|
|
|
||||||
|
|
@ -851,6 +851,13 @@ void LLPanelRegionInfo::initCtrl(const std::string& name)
|
||||||
getChild<LLUICtrl>(name)->setCommitCallback(boost::bind(&LLPanelRegionInfo::onChangeAnything, this));
|
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>
|
template<typename CTRL>
|
||||||
void LLPanelRegionInfo::initAndSetCtrl(CTRL*& ctrl, const std::string& name)
|
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)
|
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])
|
if (mTextureDetailCtrl[i])
|
||||||
{
|
{
|
||||||
mTextureDetailCtrl[i]->setBakeTextureEnabled(false);
|
mTextureDetailCtrl[i]->setBakeTextureEnabled(false);
|
||||||
|
|
|
||||||
|
|
@ -159,6 +159,7 @@ public:
|
||||||
protected:
|
protected:
|
||||||
void initCtrl(const std::string& name);
|
void initCtrl(const std::string& name);
|
||||||
template<typename CTRL> void initAndSetCtrl(CTRL*& ctrl, 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
|
// Returns true if update sent and apply button should be
|
||||||
// disabled.
|
// disabled.
|
||||||
|
|
|
||||||
|
|
@ -414,7 +414,11 @@ void LLGiveInventory::commitGiveInventoryItem(const LLUUID& to_agent,
|
||||||
effectp->setTargetObject(gObjectList.findObject(to_agent));
|
effectp->setTargetObject(gObjectList.findObject(to_agent));
|
||||||
effectp->setDuration(LL_HUD_DUR_SHORT);
|
effectp->setDuration(LL_HUD_DUR_SHORT);
|
||||||
effectp->setColor(LLColor4U(gAgent.getEffectColor()));
|
effectp->setColor(LLColor4U(gAgent.getEffectColor()));
|
||||||
gFloaterTools->dirty();
|
|
||||||
|
if (gFloaterTools)
|
||||||
|
{
|
||||||
|
gFloaterTools->dirty();
|
||||||
|
}
|
||||||
|
|
||||||
LLMuteList::getInstance()->autoRemove(to_agent, LLMuteList::AR_INVENTORY);
|
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->setTargetObject(gObjectList.findObject(to_agent));
|
||||||
effectp->setDuration(LL_HUD_DUR_SHORT);
|
effectp->setDuration(LL_HUD_DUR_SHORT);
|
||||||
effectp->setColor(LLColor4U(gAgent.getEffectColor()));
|
effectp->setColor(LLColor4U(gAgent.getEffectColor()));
|
||||||
gFloaterTools->dirty();
|
|
||||||
|
if (gFloaterTools)
|
||||||
|
{
|
||||||
|
gFloaterTools->dirty();
|
||||||
|
}
|
||||||
|
|
||||||
LLMuteList::getInstance()->autoRemove(to_agent, LLMuteList::AR_INVENTORY);
|
LLMuteList::getInstance()->autoRemove(to_agent, LLMuteList::AR_INVENTORY);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,17 @@ void LLHeroProbeManager::update()
|
||||||
return;
|
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_ZONE_SCOPED_CATEGORY_DISPLAY;
|
||||||
LL_PROFILE_GPU_ZONE("hero manager update");
|
LL_PROFILE_GPU_ZONE("hero manager update");
|
||||||
llassert(!gCubeSnapshot); // assert a snapshot is not in progress
|
llassert(!gCubeSnapshot); // assert a snapshot is not in progress
|
||||||
|
|
|
||||||
|
|
@ -144,6 +144,7 @@ private:
|
||||||
std::vector<LLPointer<LLVOVolume>> mHeroVOList;
|
std::vector<LLPointer<LLVOVolume>> mHeroVOList;
|
||||||
LLPointer<LLVOVolume> mNearestHero;
|
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()));
|
asset_type = (LLAssetType::EType)(atoi((*(iter++)).c_str()));
|
||||||
iter++; // wearable type if applicable, otherwise asset type
|
iter++; // wearable type if applicable, otherwise asset type
|
||||||
item_name = std::string((*(iter++)).c_str());
|
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
|
else
|
||||||
|
|
|
||||||
|
|
@ -3550,6 +3550,7 @@ void LLIMMgr::inviteToSession(
|
||||||
&& voice_invite && "VoiceInviteQuestionDefault" == question_type)
|
&& voice_invite && "VoiceInviteQuestionDefault" == question_type)
|
||||||
{
|
{
|
||||||
LL_INFOS("IMVIEW") << "Rejecting voice call from initiating muted resident " << caller_name << LL_ENDL;
|
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);
|
LLIncomingCallDialog::processCallResponse(1, payload);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -3598,6 +3599,7 @@ void LLIMMgr::inviteToSession(
|
||||||
send_do_not_disturb_message(gMessageSystem, caller_id, session_id);
|
send_do_not_disturb_message(gMessageSystem, caller_id, session_id);
|
||||||
}
|
}
|
||||||
// silently decline the call
|
// silently decline the call
|
||||||
|
payload["voice_channel_info"] = voice_channel_info;
|
||||||
LLIncomingCallDialog::processCallResponse(1, payload);
|
LLIncomingCallDialog::processCallResponse(1, payload);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -844,7 +844,7 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id,
|
||||||
disabled_items.push_back(std::string("Copy"));
|
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("New folder from selected"));
|
||||||
items.push_back(std::string("Subfolder Separator"));
|
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,
|
// Note: creation will take time, so passing folder id to callback is slightly unreliable,
|
||||||
// but so is collecting and passing descendants' ids
|
// 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,
|
gInventory.createNewCategory(dest_id,
|
||||||
LLFolderType::FT_OUTFIT,
|
LLFolderType::FT_OUTFIT,
|
||||||
inv_cat->getName(),
|
inv_cat->getName(),
|
||||||
|
|
@ -5323,11 +5323,25 @@ void LLFolderBridge::dropToMyOutfits(LLInventoryCategory* inv_cat, LLPointer<LLI
|
||||||
inv_cat->getThumbnailUUID());
|
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::cat_array_t* categories;
|
||||||
LLInventoryModel::item_array_t* items;
|
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;
|
LLInventoryObject::const_object_list_t link_array;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -378,7 +378,10 @@ public:
|
||||||
static void staticFolderOptionsMenu();
|
static void staticFolderOptionsMenu();
|
||||||
|
|
||||||
protected:
|
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 callback_pasteFromClipboard(const LLSD& notification, const LLSD& response);
|
||||||
void perform_pasteFromClipboard();
|
void perform_pasteFromClipboard();
|
||||||
void gatherMessage(std::string& message, S32 depth, LLError::ELevel log_level);
|
void gatherMessage(std::string& message, S32 depth, LLError::ELevel log_level);
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@
|
||||||
#include "llaisapi.h"
|
#include "llaisapi.h"
|
||||||
#include "llagent.h"
|
#include "llagent.h"
|
||||||
#include "llappviewer.h"
|
#include "llappviewer.h"
|
||||||
|
#include "llappearancemgr.h"
|
||||||
#include "llcallbacklist.h"
|
#include "llcallbacklist.h"
|
||||||
#include "llinventorymodel.h"
|
#include "llinventorymodel.h"
|
||||||
#include "llinventorypanel.h"
|
#include "llinventorypanel.h"
|
||||||
|
|
@ -470,6 +471,22 @@ void LLInventoryModelBackgroundFetch::fetchCOF(nullary_func_t callback)
|
||||||
callback();
|
callback();
|
||||||
LLUUID cat_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
|
LLUUID cat_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
|
||||||
LLInventoryModelBackgroundFetch::getInstance()->onAISFolderCalback(cat_id, id, FT_DEFAULT);
|
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
|
// 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)
|
if (model_item && view_item && viewmodel_item)
|
||||||
{
|
{
|
||||||
const LLUUID& idp = viewmodel_item->getUUID();
|
const LLUUID idp = viewmodel_item->getUUID();
|
||||||
view_item->destroyView();
|
view_item->destroyView();
|
||||||
removeItemID(idp);
|
removeItemID(idp);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -892,6 +892,11 @@ bool LLLogChat::isTranscriptFileFound(std::string fullname)
|
||||||
return result;
|
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
|
//*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)
|
//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>"
|
//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 isAdHocTranscriptExist(std::string file_name);
|
||||||
static bool isTranscriptFileFound(std::string fullname);
|
static bool isTranscriptFileFound(std::string fullname);
|
||||||
|
|
||||||
|
static std::string getGroupChatSuffix();
|
||||||
|
|
||||||
bool historyThreadsFinished(LLUUID session_id);
|
bool historyThreadsFinished(LLUUID session_id);
|
||||||
LLLoadHistoryThread* getLoadHistoryThread(LLUUID session_id);
|
LLLoadHistoryThread* getLoadHistoryThread(LLUUID session_id);
|
||||||
LLDeleteHistoryThread* getDeleteHistoryThread(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
|
MESH_UNKNOWN
|
||||||
} EMeshProcessingResult;
|
} 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
|
class LLMeshUploadData
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -183,7 +193,8 @@ public:
|
||||||
class RequestStats
|
class RequestStats
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RequestStats() : mRetries(0) {};
|
|
||||||
|
RequestStats() :mRetries(0) {};
|
||||||
|
|
||||||
void updateTime();
|
void updateTime();
|
||||||
bool canRetry() const;
|
bool canRetry() const;
|
||||||
|
|
@ -195,6 +206,67 @@ private:
|
||||||
LLFrameTimer mTimer;
|
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
|
class LLMeshHeader
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -235,19 +307,67 @@ public:
|
||||||
|
|
||||||
m404 = header.has("404");
|
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 mVersion = -1;
|
||||||
S32 mSkinOffset = -1;
|
S32 mSkinOffset = -1;
|
||||||
S32 mSkinSize = -1;
|
S32 mSkinSize = -1;
|
||||||
|
bool mSkinInCache = false;
|
||||||
|
|
||||||
S32 mPhysicsConvexOffset = -1;
|
S32 mPhysicsConvexOffset = -1;
|
||||||
S32 mPhysicsConvexSize = -1;
|
S32 mPhysicsConvexSize = -1;
|
||||||
|
bool mPhysicsConvexInCache = false;
|
||||||
|
|
||||||
S32 mPhysicsMeshOffset = -1;
|
S32 mPhysicsMeshOffset = -1;
|
||||||
S32 mPhysicsMeshSize = -1;
|
S32 mPhysicsMeshSize = -1;
|
||||||
|
bool mPhysicsMeshInCache = false;
|
||||||
|
|
||||||
S32 mLodOffset[4] = { -1 };
|
S32 mLodOffset[LLModel::NUM_LODS] = { -1 };
|
||||||
S32 mLodSize[4] = { -1 };
|
S32 mLodSize[LLModel::NUM_LODS] = { -1 };
|
||||||
|
bool mLodInCache[LLModel::NUM_LODS] = { false };
|
||||||
|
S32 mHeaderSize = -1;
|
||||||
|
|
||||||
bool m404 = false;
|
bool m404 = false;
|
||||||
};
|
};
|
||||||
|
|
@ -258,6 +378,7 @@ public:
|
||||||
|
|
||||||
static std::atomic<S32> sActiveHeaderRequests;
|
static std::atomic<S32> sActiveHeaderRequests;
|
||||||
static std::atomic<S32> sActiveLODRequests;
|
static std::atomic<S32> sActiveLODRequests;
|
||||||
|
static std::atomic<S32> sActiveSkinRequests;
|
||||||
static U32 sMaxConcurrentRequests;
|
static U32 sMaxConcurrentRequests;
|
||||||
static S32 sRequestLowWater;
|
static S32 sRequestLowWater;
|
||||||
static S32 sRequestHighWater;
|
static S32 sRequestHighWater;
|
||||||
|
|
@ -265,10 +386,13 @@ public:
|
||||||
|
|
||||||
LLMutex* mMutex;
|
LLMutex* mMutex;
|
||||||
LLMutex* mHeaderMutex;
|
LLMutex* mHeaderMutex;
|
||||||
|
LLMutex* mLoadedMutex;
|
||||||
|
LLMutex* mPendingMutex;
|
||||||
|
LLMutex* mSkinMapMutex;
|
||||||
LLCondition* mSignal;
|
LLCondition* mSignal;
|
||||||
|
|
||||||
//map of known mesh headers
|
//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;
|
mesh_header_map mMeshHeader;
|
||||||
|
|
||||||
class HeaderRequest : public RequestStats
|
class HeaderRequest : public RequestStats
|
||||||
|
|
@ -292,22 +416,13 @@ public:
|
||||||
public:
|
public:
|
||||||
LLVolumeParams mMeshParams;
|
LLVolumeParams mMeshParams;
|
||||||
S32 mLOD;
|
S32 mLOD;
|
||||||
F32 mScore;
|
|
||||||
|
|
||||||
LODRequest(const LLVolumeParams& mesh_params, S32 lod)
|
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
|
class UUIDBasedRequest : public RequestStats
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -369,7 +484,7 @@ public:
|
||||||
std::deque<LoadedMesh> mLoadedQ;
|
std::deque<LoadedMesh> mLoadedQ;
|
||||||
|
|
||||||
//map of pending header requests and currently desired LODs
|
//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;
|
pending_lod_map mPendingLOD;
|
||||||
|
|
||||||
// map of mesh ID to skin info (mirrors LLMeshRepository::mSkinMap)
|
// map of mesh ID to skin info (mirrors LLMeshRepository::mSkinMap)
|
||||||
|
|
@ -379,6 +494,8 @@ public:
|
||||||
|
|
||||||
// workqueue for processing generic requests
|
// workqueue for processing generic requests
|
||||||
LL::WorkQueue mWorkQueue;
|
LL::WorkQueue mWorkQueue;
|
||||||
|
// lods have their own thread due to costly cacheOptimize() calls
|
||||||
|
std::unique_ptr<LL::ThreadPool> mMeshThreadPool;
|
||||||
|
|
||||||
// llcorehttp library interface objects.
|
// llcorehttp library interface objects.
|
||||||
LLCore::HttpStatus mHttpStatus;
|
LLCore::HttpStatus mHttpStatus;
|
||||||
|
|
@ -402,16 +519,16 @@ public:
|
||||||
void lockAndLoadMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
|
void lockAndLoadMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
|
||||||
void loadMeshLOD(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 fetchMeshHeader(const LLVolumeParams& mesh_params);
|
||||||
bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, bool can_retry = true);
|
bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
|
||||||
EMeshProcessingResult headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size);
|
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);
|
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 skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size);
|
||||||
bool decompositionReceived(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);
|
EMeshProcessingResult physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 data_size);
|
||||||
bool hasPhysicsShapeInHeader(const LLUUID& mesh_id);
|
bool hasPhysicsShapeInHeader(const LLUUID& mesh_id) const;
|
||||||
bool hasSkinInfoInHeader(const LLUUID& mesh_id);
|
bool hasSkinInfoInHeader(const LLUUID& mesh_id) const;
|
||||||
bool hasHeader(const LLUUID& mesh_id);
|
bool hasHeader(const LLUUID& mesh_id) const;
|
||||||
|
|
||||||
void notifyLoadedMeshes();
|
void notifyLoadedMeshes();
|
||||||
S32 getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
|
S32 getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
|
||||||
|
|
@ -422,7 +539,7 @@ public:
|
||||||
|
|
||||||
//send request for skin info, returns true if header info exists
|
//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)
|
// (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
|
//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)
|
// (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 decActiveLODRequests();
|
||||||
static void incActiveHeaderRequests();
|
static void incActiveHeaderRequests();
|
||||||
static void decActiveHeaderRequests();
|
static void decActiveHeaderRequests();
|
||||||
|
static void incActiveSkinRequests();
|
||||||
|
static void decActiveSkinRequests();
|
||||||
|
|
||||||
// Set the caps strings and preferred version for constructing
|
// Set the caps strings and preferred version for constructing
|
||||||
// mesh fetch URLs.
|
// mesh fetch URLs.
|
||||||
|
|
@ -456,6 +575,14 @@ private:
|
||||||
LLCore::HttpHandle getByteRange(const std::string & url,
|
LLCore::HttpHandle getByteRange(const std::string & url,
|
||||||
size_t offset, size_t len,
|
size_t offset, size_t len,
|
||||||
const LLCore::HttpHandler::ptr_t &handler);
|
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);
|
bool init(const LLMeshHeader& header);
|
||||||
|
|
||||||
// Size for given LOD
|
// Size for given LOD
|
||||||
S32 getSizeByLOD(S32 lod);
|
S32 getSizeByLOD(S32 lod) const;
|
||||||
|
|
||||||
// Sum of all LOD sizes.
|
// Sum of all LOD sizes.
|
||||||
S32 getSizeTotal();
|
S32 getSizeTotal() const;
|
||||||
|
|
||||||
// Estimated triangle counts for the given LOD.
|
// 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
|
// Estimated triangle counts for the largest LOD. Typically this
|
||||||
// is also the "high" LOD, but not necessarily.
|
// is also the "high" LOD, but not necessarily.
|
||||||
F32 getEstTrisMax();
|
F32 getEstTrisMax() const;
|
||||||
|
|
||||||
// Triangle count as computed by original streaming cost
|
// Triangle count as computed by original streaming cost
|
||||||
// formula. Triangles in each LOD are weighted based on how
|
// formula. Triangles in each LOD are weighted based on how
|
||||||
// frequently they will be seen.
|
// frequently they will be seen.
|
||||||
// This was called "unscaled_value" in the original getStreamingCost() functions.
|
// 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
|
// Triangle count used by triangle-based cost formula. Based on
|
||||||
// triangles in highest LOD plus potentially partial charges for
|
// triangles in highest LOD plus potentially partial charges for
|
||||||
// lower LODs depending on complexity.
|
// lower LODs depending on complexity.
|
||||||
F32 getEstTrisForStreamingCost();
|
F32 getEstTrisForStreamingCost() const;
|
||||||
|
|
||||||
// Streaming cost. This should match the server-side calculation
|
// Streaming cost. This should match the server-side calculation
|
||||||
// for the corresponding volume.
|
// for the corresponding volume.
|
||||||
F32 getRadiusBasedStreamingCost(F32 radius);
|
F32 getRadiusBasedStreamingCost(F32 radius) const;
|
||||||
|
|
||||||
// New streaming cost formula, currently only used for animated objects.
|
// New streaming cost formula, currently only used for animated objects.
|
||||||
F32 getTriangleBasedStreamingCost();
|
F32 getTriangleBasedStreamingCost() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// From the "size" field of the mesh header. LOD 0=lowest, 3=highest.
|
// From the "size" field of the mesh header. LOD 0=lowest, 3=highest.
|
||||||
|
|
@ -620,12 +747,12 @@ public:
|
||||||
static U32 sLODPending;
|
static U32 sLODPending;
|
||||||
static U32 sLODProcessing;
|
static U32 sLODProcessing;
|
||||||
static U32 sCacheBytesRead;
|
static U32 sCacheBytesRead;
|
||||||
static U32 sCacheBytesWritten;
|
static std::atomic<U32> sCacheBytesWritten;
|
||||||
static U32 sCacheBytesHeaders;
|
static U32 sCacheBytesHeaders;
|
||||||
static U32 sCacheBytesSkins;
|
static U32 sCacheBytesSkins;
|
||||||
static U32 sCacheBytesDecomps;
|
static U32 sCacheBytesDecomps;
|
||||||
static U32 sCacheReads;
|
static U32 sCacheReads;
|
||||||
static U32 sCacheWrites;
|
static std::atomic<U32> sCacheWrites;
|
||||||
static U32 sMaxLockHoldoffs; // Maximum sequential locking failures
|
static U32 sMaxLockHoldoffs; // Maximum sequential locking failures
|
||||||
|
|
||||||
static LLDeadmanTimer sQuiescentTimer; // Time-to-complete-mesh-downloads after significant events
|
static LLDeadmanTimer sQuiescentTimer; // Time-to-complete-mesh-downloads after significant events
|
||||||
|
|
@ -646,11 +773,11 @@ public:
|
||||||
|
|
||||||
void unregisterMesh(LLVOVolume* volume);
|
void unregisterMesh(LLVOVolume* volume);
|
||||||
//mesh management functions
|
//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 notifyLoadedMeshes();
|
||||||
void notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume);
|
void notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume, S32 lod);
|
||||||
void notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod);
|
void notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 request_lod, S32 volume_lod);
|
||||||
void notifySkinInfoReceived(LLMeshSkinInfo* info);
|
void notifySkinInfoReceived(LLMeshSkinInfo* info);
|
||||||
void notifySkinInfoUnavailable(const LLUUID& info);
|
void notifySkinInfoUnavailable(const LLUUID& info);
|
||||||
void notifyDecompositionReceived(LLModel::Decomposition* info);
|
void notifyDecompositionReceived(LLModel::Decomposition* info);
|
||||||
|
|
@ -662,7 +789,7 @@ public:
|
||||||
void fetchPhysicsShape(const LLUUID& mesh_id);
|
void fetchPhysicsShape(const LLUUID& mesh_id);
|
||||||
bool hasPhysicsShape(const LLUUID& mesh_id);
|
bool hasPhysicsShape(const LLUUID& mesh_id);
|
||||||
bool hasSkinInfo(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 buildHull(const LLVolumeParams& params, S32 detail);
|
||||||
void buildPhysicsMesh(LLModel::Decomposition& decomp);
|
void buildPhysicsMesh(LLModel::Decomposition& decomp);
|
||||||
|
|
@ -676,7 +803,7 @@ public:
|
||||||
LLHandle<LLWholeModelFeeObserver> fee_observer= (LLHandle<LLWholeModelFeeObserver>()),
|
LLHandle<LLWholeModelFeeObserver> fee_observer= (LLHandle<LLWholeModelFeeObserver>()),
|
||||||
LLHandle<LLWholeModelUploadObserver> upload_observer = (LLHandle<LLWholeModelUploadObserver>()));
|
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.
|
// Quiescent timer management, main thread only.
|
||||||
static void metricsStart();
|
static void metricsStart();
|
||||||
|
|
@ -684,7 +811,7 @@ public:
|
||||||
static void metricsProgress(unsigned int count);
|
static void metricsProgress(unsigned int count);
|
||||||
static void metricsUpdate();
|
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];
|
mesh_load_map mLoadingMeshes[4];
|
||||||
|
|
||||||
typedef std::unordered_map<LLUUID, LLPointer<LLMeshSkinInfo>> skin_map;
|
typedef std::unordered_map<LLUUID, LLPointer<LLMeshSkinInfo>> skin_map;
|
||||||
|
|
@ -695,15 +822,13 @@ public:
|
||||||
|
|
||||||
LLMutex* mMeshMutex;
|
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
|
//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;
|
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
|
//list of mesh ids awaiting decompositions
|
||||||
std::unordered_set<LLUUID> mLoadingDecompositions;
|
std::unordered_set<LLUUID> mLoadingDecompositions;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -92,10 +92,18 @@ void LLHandlerUtil::logToIM(const EInstantMessage& session_type,
|
||||||
from = SYSTEM_FROM;
|
from = SYSTEM_FROM;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build a new format username or firstname_lastname for legacy names
|
std::string file_name;
|
||||||
// to use it for a history log filename.
|
if (session_type == IM_SESSION_GROUP_START)
|
||||||
std::string user_name = LLCacheName::buildUsername(session_name);
|
{
|
||||||
LLIMModel::instance().logToFile(user_name, from, from_id, message);
|
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
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue