# Conflicts:
#	indra/newview/llappviewer.cpp
#	indra/newview/llviewerdisplay.cpp
#	indra/newview/llviewermedia.cpp
master
Ansariel 2024-09-19 23:04:19 +02:00
commit 193fb1627f
32 changed files with 747 additions and 175 deletions

View File

@ -5,6 +5,8 @@
a0b3021bdcf76859054fda8e30abb3ed47749e83
8444cd9562a6a7b755fcb075864e205122354192
863c541ce0b2e3e1e566cc88423d3e87aaedb6ca
743a1a6d8eabf069d95777c96e5b657cb8702593
4a00da1ada89af6f313cee30f5177634b0b180a9
# Wrong line endings
1b67dd855c41f5a0cda7ec2a68d98071986ca703
6cc7dd09d5e69cf57e6de7fb568a0ad2693f9c9a

View File

@ -514,15 +514,6 @@ then
fi
fi
# Some of the uploads takes a long time to finish in the codeticket backend,
# causing the next codeticket upload attempt to fail.
# Inserting this after each potentially large upload may prevent those errors.
# JJ is making changes to Codeticket that we hope will eliminate this failure, then this can be removed
wait_for_codeticket()
{
sleep $(( 60 * 6 ))
}
# check status and upload results to S3
if $succeeded
then

View File

@ -119,6 +119,7 @@ set(llcommon_HEADER_FILES
commoncontrol.h
ctype_workaround.h
fix_macros.h
fsyspath.h
function_types.h
indra_constants.h
lazyeventapi.h

79
indra/llcommon/fsyspath.h Normal file
View File

@ -0,0 +1,79 @@
/**
* @file fsyspath.h
* @author Nat Goodspeed
* @date 2024-04-03
* @brief Adapt our UTF-8 std::strings for std::filesystem::path
*
* $LicenseInfo:firstyear=2024&license=viewerlgpl$
* Copyright (c) 2024, Linden Research, Inc.
* $/LicenseInfo$
*/
#if ! defined(LL_FSYSPATH_H)
#define LL_FSYSPATH_H
#include <filesystem>
// While std::filesystem::path can be directly constructed from std::string on
// both Posix and Windows, that's not what we want on Windows. Per
// https://en.cppreference.com/w/cpp/filesystem/path/path:
// ... the method of conversion to the native character set depends on the
// character type used by source.
//
// * If the source character type is char, the encoding of the source is
// assumed to be the native narrow encoding (so no conversion takes place on
// POSIX systems).
// * If the source character type is char8_t, conversion from UTF-8 to native
// filesystem encoding is used. (since C++20)
// * If the source character type is wchar_t, the input is assumed to be the
// native wide encoding (so no conversion takes places on Windows).
// The trouble is that on Windows, from std::string ("source character type is
// char"), the "native narrow encoding" isn't UTF-8, so file paths containing
// non-ASCII characters get mangled.
//
// Once we're building with C++20, we could pass a UTF-8 std::string through a
// vector<char8_t> to engage std::filesystem::path's own UTF-8 conversion. But
// sigh, as of 2024-04-03 we're not yet there.
//
// Anyway, encapsulating the important UTF-8 conversions in our own subclass
// allows us to migrate forward to C++20 conventions without changing
// referencing code.
class fsyspath: public std::filesystem::path
{
using super = std::filesystem::path;
public:
// default
fsyspath() {}
// construct from UTF-8 encoded std::string
fsyspath(const std::string& path): super(std::filesystem::u8path(path)) {}
// construct from UTF-8 encoded const char*
fsyspath(const char* path): super(std::filesystem::u8path(path)) {}
// construct from existing path
fsyspath(const super& path): super(path) {}
fsyspath& operator=(const super& p) { super::operator=(p); return *this; }
fsyspath& operator=(const std::string& p)
{
super::operator=(std::filesystem::u8path(p));
return *this;
}
fsyspath& operator=(const char* p)
{
super::operator=(std::filesystem::u8path(p));
return *this;
}
// shadow base-class string() method with UTF-8 aware method
std::string string() const { return super::u8string(); }
// On Posix systems, where value_type is already char, this operator
// std::string() method shadows the base class operator string_type()
// method. But on Windows, where value_type is wchar_t, the base class
// doesn't have operator std::string(). Provide it.
operator std::string() const { return string(); }
};
#endif /* ! defined(LL_FSYSPATH_H) */

106
indra/llcommon/hexdump.h Executable file
View File

@ -0,0 +1,106 @@
/**
* @file hexdump.h
* @author Nat Goodspeed
* @date 2023-10-03
* @brief iostream manipulators to stream hex, or string with nonprinting chars
*
* $LicenseInfo:firstyear=2023&license=viewerlgpl$
* Copyright (c) 2023, Linden Research, Inc.
* $/LicenseInfo$
*/
#if ! defined(LL_HEXDUMP_H)
#define LL_HEXDUMP_H
#include <cctype>
#include <iomanip>
#include <iostream>
#include <string_view>
namespace LL
{
// Format a given byte string as 2-digit hex values, no separators
// Usage: std::cout << hexdump(somestring) << ...
class hexdump
{
public:
hexdump(const std::string_view& data):
hexdump(data.data(), data.length())
{}
hexdump(const char* data, size_t len):
hexdump(reinterpret_cast<const unsigned char*>(data), len)
{}
hexdump(const std::vector<unsigned char>& data):
hexdump(data.data(), data.size())
{}
hexdump(const unsigned char* data, size_t len):
mData(data, data + len)
{}
friend std::ostream& operator<<(std::ostream& out, const hexdump& self)
{
auto oldfmt{ out.flags() };
auto oldfill{ out.fill() };
out.setf(std::ios_base::hex, std::ios_base::basefield);
out.fill('0');
for (auto c : self.mData)
{
out << std::setw(2) << unsigned(c);
}
out.setf(oldfmt, std::ios_base::basefield);
out.fill(oldfill);
return out;
}
private:
std::vector<unsigned char> mData;
};
// Format a given byte string as a mix of printable characters and, for each
// non-printable character, "\xnn"
// Usage: std::cout << hexmix(somestring) << ...
class hexmix
{
public:
hexmix(const std::string_view& data):
mData(data)
{}
hexmix(const char* data, size_t len):
mData(data, len)
{}
friend std::ostream& operator<<(std::ostream& out, const hexmix& self)
{
auto oldfmt{ out.flags() };
auto oldfill{ out.fill() };
out.setf(std::ios_base::hex, std::ios_base::basefield);
out.fill('0');
for (auto c : self.mData)
{
// std::isprint() must be passed an unsigned char!
if (std::isprint(static_cast<unsigned char>(c)))
{
out << c;
}
else
{
out << "\\x" << std::setw(2) << unsigned(c);
}
}
out.setf(oldfmt, std::ios_base::basefield);
out.fill(oldfill);
return out;
}
private:
std::string mData;
};
} // namespace LL
#endif /* ! defined(LL_HEXDUMP_H) */

View File

@ -88,10 +88,6 @@ LLApp* LLApp::sApplication = NULL;
// and disables crashlogger
bool LLApp::sDisableCrashlogger = false;
// Local flag for whether or not to do logging in signal handlers.
//static
bool LLApp::sLogInSignal = true;
// static
// Keeps track of application status
LLScalarCond<LLApp::EAppStatus> LLApp::sStatus{LLApp::APP_STATUS_STOPPED};
@ -602,6 +598,10 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *)
// We do the somewhat sketchy operation of blocking in here until the error handler
// has gracefully stopped the app.
// FIXME(brad) - we are using this handler for asynchronous signals as well, so sLogInSignal is currently
// disabled for safety. we need to find a way to selectively reenable it when it is safe.
// see issue secondlife/viewer#2566
if (LLApp::sLogInSignal)
{
LL_INFOS() << "Signal handler - Got signal " << signum << " - " << apr_signal_description_get(signum) << LL_ENDL;

View File

@ -339,8 +339,12 @@ private:
friend void default_unix_signal_handler(int signum, siginfo_t *info, void *);
#endif
public:
static bool sLogInSignal;
private:
#ifdef LL_RELEASE_FOR_DOWNLOAD
static constexpr bool sLogInSignal = false;
#else
static constexpr bool sLogInSignal = true;
#endif
};
#endif // LL_LLAPP_H

View File

@ -194,6 +194,18 @@ public:
mSharedMutex->unlock<SHARED>();
}
void lock()
{
if (mSharedMutex)
mSharedMutex->lock<SHARED>();
}
void unlock()
{
if (mSharedMutex)
mSharedMutex->unlock<SHARED>();
}
private:
LLSharedMutex* mSharedMutex;
};

View File

@ -61,12 +61,20 @@ LLSD LlsdFromJson(const boost::json::value& val)
result = LLSD(val.as_bool());
break;
case boost::json::kind::array:
{
result = LLSD::emptyArray();
for (const auto &element : val.as_array())
auto& array = val.as_array();
// allocate elements 0 .. (size() - 1) to avoid incremental allocation
if (! array.empty())
{
result[array.size() - 1] = LLSD();
}
for (const auto &element : array)
{
result.append(LlsdFromJson(element));
}
break;
}
case boost::json::kind::object:
result = LLSD::emptyMap();
for (const auto& element : val.as_object())
@ -106,6 +114,7 @@ boost::json::value LlsdToJson(const LLSD &val)
case LLSD::TypeMap:
{
boost::json::object& obj = result.emplace_object();
obj.reserve(val.size());
for (const auto& llsd_dat : llsd::inMap(val))
{
obj[llsd_dat.first] = LlsdToJson(llsd_dat.second);
@ -115,6 +124,7 @@ boost::json::value LlsdToJson(const LLSD &val)
case LLSD::TypeArray:
{
boost::json::array& json_array = result.emplace_array();
json_array.reserve(val.size());
for (const auto& llsd_dat : llsd::inArray(val))
{
json_array.push_back(LlsdToJson(llsd_dat));
@ -123,7 +133,8 @@ boost::json::value LlsdToJson(const LLSD &val)
}
case LLSD::TypeBinary:
default:
LL_ERRS("LlsdToJson") << "Unsupported conversion to JSON from LLSD type (" << val.type() << ")." << LL_ENDL;
LL_ERRS("LlsdToJson") << "Unsupported conversion to JSON from LLSD type ("
<< val.type() << ")." << LL_ENDL;
break;
}

View File

@ -179,7 +179,7 @@ private:
public:
template<bool SHARED>
class DataLock : LLSharedMutexLockTemplate<SHARED>
class DataLock : public LLSharedMutexLockTemplate<SHARED>
{
public:
DataLock(const LLImageBase* image)

View File

@ -271,7 +271,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons
const LLFontGlyphInfo* next_glyph = NULL;
static constexpr S32 GLYPH_BATCH_SIZE = 30;
static thread_local LLVector3 vertices[GLYPH_BATCH_SIZE * 6];
static thread_local LLVector4a vertices[GLYPH_BATCH_SIZE * 6];
static thread_local LLVector2 uvs[GLYPH_BATCH_SIZE * 6];
static thread_local LLColor4U colors[GLYPH_BATCH_SIZE * 6];
@ -1334,42 +1334,42 @@ LLFontGL &LLFontGL::operator=(const LLFontGL &source)
return *this;
}
void LLFontGL::renderTriangle(LLVector3* vertex_out, LLVector2* uv_out, LLColor4U* colors_out, const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4U& color, F32 slant_amt) const
void LLFontGL::renderTriangle(LLVector4a* vertex_out, LLVector2* uv_out, LLColor4U* colors_out, const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4U& color, F32 slant_amt) const
{
S32 index = 0;
vertex_out[index] = LLVector3(screen_rect.mRight, screen_rect.mTop, 0.f);
uv_out[index] = LLVector2(uv_rect.mRight, uv_rect.mTop);
vertex_out[index].set(screen_rect.mRight, screen_rect.mTop, 0.f);
uv_out[index].set(uv_rect.mRight, uv_rect.mTop);
colors_out[index] = color;
index++;
vertex_out[index] = LLVector3(screen_rect.mLeft, screen_rect.mTop, 0.f);
uv_out[index] = LLVector2(uv_rect.mLeft, uv_rect.mTop);
vertex_out[index].set(screen_rect.mLeft, screen_rect.mTop, 0.f);
uv_out[index].set(uv_rect.mLeft, uv_rect.mTop);
colors_out[index] = color;
index++;
vertex_out[index] = LLVector3(screen_rect.mLeft, screen_rect.mBottom, 0.f);
uv_out[index] = LLVector2(uv_rect.mLeft, uv_rect.mBottom);
vertex_out[index].set(screen_rect.mLeft, screen_rect.mBottom, 0.f);
uv_out[index].set(uv_rect.mLeft, uv_rect.mBottom);
colors_out[index] = color;
index++;
vertex_out[index] = LLVector3(screen_rect.mRight, screen_rect.mTop, 0.f);
uv_out[index] = LLVector2(uv_rect.mRight, uv_rect.mTop);
vertex_out[index].set(screen_rect.mRight, screen_rect.mTop, 0.f);
uv_out[index].set(uv_rect.mRight, uv_rect.mTop);
colors_out[index] = color;
index++;
vertex_out[index] = LLVector3(screen_rect.mLeft, screen_rect.mBottom, 0.f);
uv_out[index] = LLVector2(uv_rect.mLeft, uv_rect.mBottom);
vertex_out[index].set(screen_rect.mLeft, screen_rect.mBottom, 0.f);
uv_out[index].set(uv_rect.mLeft, uv_rect.mBottom);
colors_out[index] = color;
index++;
vertex_out[index] = LLVector3(screen_rect.mRight, screen_rect.mBottom, 0.f);
uv_out[index] = LLVector2(uv_rect.mRight, uv_rect.mBottom);
vertex_out[index].set(screen_rect.mRight, screen_rect.mBottom, 0.f);
uv_out[index].set(uv_rect.mRight, uv_rect.mBottom);
colors_out[index] = color;
}
void LLFontGL::drawGlyph(S32& glyph_count, LLVector3* vertex_out, LLVector2* uv_out, LLColor4U* colors_out, const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4U& color, U8 style, ShadowType shadow, F32 drop_shadow_strength) const
void LLFontGL::drawGlyph(S32& glyph_count, LLVector4a* vertex_out, LLVector2* uv_out, LLColor4U* colors_out, const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4U& color, U8 style, ShadowType shadow, F32 drop_shadow_strength) const
{
F32 slant_offset;
slant_offset = ((style & ITALIC) ? ( -mFontFreetype->getAscenderHeight() * 0.2f) : 0.f);

View File

@ -249,8 +249,8 @@ private:
LLFontDescriptor mFontDescriptor;
LLPointer<LLFontFreetype> mFontFreetype;
void renderTriangle(LLVector3* vertex_out, LLVector2* uv_out, LLColor4U* colors_out, const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4U& color, F32 slant_amt) const;
void drawGlyph(S32& glyph_count, LLVector3* vertex_out, LLVector2* uv_out, LLColor4U* colors_out, const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4U& color, U8 style, ShadowType shadow, F32 drop_shadow_fade) const;
void renderTriangle(LLVector4a* vertex_out, LLVector2* uv_out, LLColor4U* colors_out, const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4U& color, F32 slant_amt) const;
void drawGlyph(S32& glyph_count, LLVector4a* vertex_out, LLVector2* uv_out, LLColor4U* colors_out, const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4U& color, U8 style, ShadowType shadow, F32 drop_shadow_fade) const;
// Registry holds all instantiated fonts.
static LLFontRegistry* sFontRegistry;

View File

@ -63,6 +63,7 @@ U64 LLGLSLShader::sTotalTimeElapsed = 0;
U32 LLGLSLShader::sTotalTrianglesDrawn = 0;
U64 LLGLSLShader::sTotalSamplesDrawn = 0;
U32 LLGLSLShader::sTotalBinds = 0;
boost::json::value LLGLSLShader::sDefaultStats;
//UI shader -- declared here so llui_libtest will link properly
LLGLSLShader gUIProgram;
@ -101,9 +102,9 @@ void LLGLSLShader::initProfile()
sTotalSamplesDrawn = 0;
sTotalBinds = 0;
for (std::set<LLGLSLShader*>::iterator iter = sInstances.begin(); iter != sInstances.end(); ++iter)
for (auto ptr : sInstances)
{
(*iter)->clearStats();
ptr->clearStats();
}
}
@ -117,45 +118,57 @@ struct LLGLSLShaderCompareTimeElapsed
};
//static
void LLGLSLShader::finishProfile(bool emit_report)
void LLGLSLShader::finishProfile(boost::json::value& statsv)
{
sProfileEnabled = false;
if (emit_report)
if (! statsv.is_null())
{
std::vector<LLGLSLShader*> sorted;
for (std::set<LLGLSLShader*>::iterator iter = sInstances.begin(); iter != sInstances.end(); ++iter)
{
sorted.push_back(*iter);
}
std::vector<LLGLSLShader*> sorted(sInstances.begin(), sInstances.end());
std::sort(sorted.begin(), sorted.end(), LLGLSLShaderCompareTimeElapsed());
auto& stats = statsv.as_object();
auto shadersit = stats.emplace("shaders", boost::json::array_kind).first;
auto& shaders = shadersit->value().as_array();
bool unbound = false;
for (std::vector<LLGLSLShader*>::iterator iter = sorted.begin(); iter != sorted.end(); ++iter)
for (auto ptr : sorted)
{
(*iter)->dumpStats();
if ((*iter)->mBinds == 0)
if (ptr->mBinds == 0)
{
unbound = true;
}
else
{
auto& shaderit = shaders.emplace_back(boost::json::object_kind);
ptr->dumpStats(shaderit.as_object());
}
}
constexpr float mega = 1'000'000.f;
float totalTimeMs = sTotalTimeElapsed / mega;
LL_INFOS() << "-----------------------------------" << LL_ENDL;
LL_INFOS() << "Total rendering time: " << llformat("%.4f ms", sTotalTimeElapsed / 1000000.f) << LL_ENDL;
LL_INFOS() << "Total samples drawn: " << llformat("%.4f million", sTotalSamplesDrawn / 1000000.f) << LL_ENDL;
LL_INFOS() << "Total triangles drawn: " << llformat("%.3f million", sTotalTrianglesDrawn / 1000000.f) << LL_ENDL;
LL_INFOS() << "Total rendering time: " << llformat("%.4f ms", totalTimeMs) << LL_ENDL;
LL_INFOS() << "Total samples drawn: " << llformat("%.4f million", sTotalSamplesDrawn / mega) << LL_ENDL;
LL_INFOS() << "Total triangles drawn: " << llformat("%.3f million", sTotalTrianglesDrawn / mega) << LL_ENDL;
LL_INFOS() << "-----------------------------------" << LL_ENDL;
auto totalsit = stats.emplace("totals", boost::json::object_kind).first;
auto& totals = totalsit->value().as_object();
totals.emplace("time", totalTimeMs / 1000.0);
totals.emplace("binds", sTotalBinds);
totals.emplace("samples", sTotalSamplesDrawn);
totals.emplace("triangles", sTotalTrianglesDrawn);
auto unusedit = stats.emplace("unused", boost::json::array_kind).first;
auto& unused = unusedit->value().as_array();
if (unbound)
{
LL_INFOS() << "The following shaders were unused: " << LL_ENDL;
for (std::vector<LLGLSLShader*>::iterator iter = sorted.begin(); iter != sorted.end(); ++iter)
for (auto ptr : sorted)
{
if ((*iter)->mBinds == 0)
if (ptr->mBinds == 0)
{
LL_INFOS() << (*iter)->mName << LL_ENDL;
LL_INFOS() << ptr->mName << LL_ENDL;
unused.emplace_back(ptr->mName);
}
}
}
@ -170,36 +183,43 @@ void LLGLSLShader::clearStats()
mBinds = 0;
}
void LLGLSLShader::dumpStats()
void LLGLSLShader::dumpStats(boost::json::object& stats)
{
if (mBinds > 0)
stats.emplace("name", mName);
auto filesit = stats.emplace("files", boost::json::array_kind).first;
auto& files = filesit->value().as_array();
LL_INFOS() << "=============================================" << LL_ENDL;
LL_INFOS() << mName << LL_ENDL;
for (U32 i = 0; i < mShaderFiles.size(); ++i)
{
LL_INFOS() << "=============================================" << LL_ENDL;
LL_INFOS() << mName << LL_ENDL;
for (U32 i = 0; i < mShaderFiles.size(); ++i)
{
LL_INFOS() << mShaderFiles[i].first << LL_ENDL;
}
LL_INFOS() << "=============================================" << LL_ENDL;
F32 ms = mTimeElapsed / 1000000.f;
F32 seconds = ms / 1000.f;
F32 pct_tris = (F32)mTrianglesDrawn / (F32)sTotalTrianglesDrawn * 100.f;
F32 tris_sec = (F32)(mTrianglesDrawn / 1000000.0);
tris_sec /= seconds;
F32 pct_samples = (F32)((F64)mSamplesDrawn / (F64)sTotalSamplesDrawn) * 100.f;
F32 samples_sec = (F32)(mSamplesDrawn / 1000000000.0);
samples_sec /= seconds;
F32 pct_binds = (F32)mBinds / (F32)sTotalBinds * 100.f;
LL_INFOS() << "Triangles Drawn: " << mTrianglesDrawn << " " << llformat("(%.2f pct of total, %.3f million/sec)", pct_tris, tris_sec) << LL_ENDL;
LL_INFOS() << "Binds: " << mBinds << " " << llformat("(%.2f pct of total)", pct_binds) << LL_ENDL;
LL_INFOS() << "SamplesDrawn: " << mSamplesDrawn << " " << llformat("(%.2f pct of total, %.3f billion/sec)", pct_samples, samples_sec) << LL_ENDL;
LL_INFOS() << "Time Elapsed: " << mTimeElapsed << " " << llformat("(%.2f pct of total, %.5f ms)\n", (F32)((F64)mTimeElapsed / (F64)sTotalTimeElapsed) * 100.f, ms) << LL_ENDL;
LL_INFOS() << mShaderFiles[i].first << LL_ENDL;
files.emplace_back(mShaderFiles[i].first);
}
LL_INFOS() << "=============================================" << LL_ENDL;
constexpr float mega = 1'000'000.f;
constexpr double giga = 1'000'000'000.0;
F32 ms = mTimeElapsed / mega;
F32 seconds = ms / 1000.f;
F32 pct_tris = (F32)mTrianglesDrawn / (F32)sTotalTrianglesDrawn * 100.f;
F32 tris_sec = (F32)(mTrianglesDrawn / mega);
tris_sec /= seconds;
F32 pct_samples = (F32)((F64)mSamplesDrawn / (F64)sTotalSamplesDrawn) * 100.f;
F32 samples_sec = (F32)(mSamplesDrawn / giga);
samples_sec /= seconds;
F32 pct_binds = (F32)mBinds / (F32)sTotalBinds * 100.f;
LL_INFOS() << "Triangles Drawn: " << mTrianglesDrawn << " " << llformat("(%.2f pct of total, %.3f million/sec)", pct_tris, tris_sec) << LL_ENDL;
LL_INFOS() << "Binds: " << mBinds << " " << llformat("(%.2f pct of total)", pct_binds) << LL_ENDL;
LL_INFOS() << "SamplesDrawn: " << mSamplesDrawn << " " << llformat("(%.2f pct of total, %.3f billion/sec)", pct_samples, samples_sec) << LL_ENDL;
LL_INFOS() << "Time Elapsed: " << mTimeElapsed << " " << llformat("(%.2f pct of total, %.5f ms)\n", (F32)((F64)mTimeElapsed / (F64)sTotalTimeElapsed) * 100.f, ms) << LL_ENDL;
stats.emplace("time", seconds);
stats.emplace("binds", mBinds);
stats.emplace("samples", mSamplesDrawn);
stats.emplace("triangles", mTrianglesDrawn);
}
//static

View File

@ -30,6 +30,7 @@
#include "llgl.h"
#include "llrender.h"
#include "llstaticstringtable.h"
#include <boost/json.hpp>
#include <unordered_map>
class LLShaderFeatures
@ -169,14 +170,14 @@ public:
static U32 sMaxGLTFNodes;
static void initProfile();
static void finishProfile(bool emit_report = true);
static void finishProfile(boost::json::value& stats=sDefaultStats);
static void startProfile();
static void stopProfile();
void unload();
void clearStats();
void dumpStats();
void dumpStats(boost::json::object& stats);
// place query objects for profiling if profiling is enabled
// if for_runtime is true, will place timer query only whether or not profiling is enabled
@ -363,6 +364,11 @@ public:
private:
void unloadInternal();
// This must be static because finishProfile() is called at least once
// within a __try block. If we default its stats parameter to a temporary
// json::value, that temporary must be destroyed when the stack is
// unwound, which __try forbids.
static boost::json::value sDefaultStats;
};
//UI shader (declared here so llui_libtest will link properly)

View File

@ -1320,7 +1320,7 @@ void LLRender::pushUIMatrix()
{
if (mUIOffset.empty())
{
mUIOffset.push_back(LLVector3(0,0,0));
mUIOffset.emplace_back(0.f,0.f,0.f);
}
else
{
@ -1329,7 +1329,7 @@ void LLRender::pushUIMatrix()
if (mUIScale.empty())
{
mUIScale.push_back(LLVector3(1,1,1));
mUIScale.emplace_back(1.f,1.f,1.f);
}
else
{
@ -1759,7 +1759,7 @@ LLVertexBuffer* LLRender::genBuffer(U32 attribute_mask, S32 count)
vb->setBuffer();
vb->setPositionData((LLVector4a*)mVerticesp.get());
vb->setPositionData(mVerticesp.get());
if (attribute_mask & LLVertexBuffer::MAP_TEXCOORD0)
{
@ -1815,12 +1815,12 @@ void LLRender::vertex3f(const GLfloat& x, const GLfloat& y, const GLfloat& z)
if (mUIOffset.empty())
{
mVerticesp[mCount] = LLVector3(x,y,z);
mVerticesp[mCount].set(x,y,z);
}
else
{
LLVector3 vert = (LLVector3(x,y,z)+mUIOffset.back()).scaledVec(mUIScale.back());
mVerticesp[mCount] = vert;
mVerticesp[mCount].set(vert.mV[VX], vert.mV[VY], vert.mV[VZ]);
}
mCount++;
@ -1829,7 +1829,7 @@ void LLRender::vertex3f(const GLfloat& x, const GLfloat& y, const GLfloat& z)
mTexcoordsp[mCount] = mTexcoordsp[mCount-1];
}
void LLRender::vertexBatchPreTransformed(LLVector3* verts, S32 vert_count)
void LLRender::vertexBatchPreTransformed(LLVector4a* verts, S32 vert_count)
{
if (mCount + vert_count > 4094)
{
@ -1850,7 +1850,7 @@ void LLRender::vertexBatchPreTransformed(LLVector3* verts, S32 vert_count)
mVerticesp[mCount] = mVerticesp[mCount-1];
}
void LLRender::vertexBatchPreTransformed(LLVector3* verts, LLVector2* uvs, S32 vert_count)
void LLRender::vertexBatchPreTransformed(LLVector4a* verts, LLVector2* uvs, S32 vert_count)
{
if (mCount + vert_count > 4094)
{
@ -1874,7 +1874,7 @@ void LLRender::vertexBatchPreTransformed(LLVector3* verts, LLVector2* uvs, S32 v
}
}
void LLRender::vertexBatchPreTransformed(LLVector3* verts, LLVector2* uvs, LLColor4U* colors, S32 vert_count)
void LLRender::vertexBatchPreTransformed(LLVector4a* verts, LLVector2* uvs, LLColor4U* colors, S32 vert_count)
{
if (mCount + vert_count > 4094)
{

View File

@ -449,9 +449,9 @@ public:
void diffuseColor4ubv(const U8* c);
void diffuseColor4ub(U8 r, U8 g, U8 b, U8 a);
void vertexBatchPreTransformed(LLVector3* verts, S32 vert_count);
void vertexBatchPreTransformed(LLVector3* verts, LLVector2* uvs, S32 vert_count);
void vertexBatchPreTransformed(LLVector3* verts, LLVector2* uvs, LLColor4U*, S32 vert_count);
void vertexBatchPreTransformed(LLVector4a* verts, S32 vert_count);
void vertexBatchPreTransformed(LLVector4a* verts, LLVector2* uvs, S32 vert_count);
void vertexBatchPreTransformed(LLVector4a* verts, LLVector2* uvs, LLColor4U*, S32 vert_count);
void setColorMask(bool writeColor, bool writeAlpha);
void setColorMask(bool writeColorR, bool writeColorG, bool writeColorB, bool writeAlpha);
@ -520,7 +520,7 @@ private:
// </FS:Ansariel>
LLPointer<LLVertexBuffer> mBuffer;
LLStrider<LLVector3> mVerticesp;
LLStrider<LLVector4a> mVerticesp;
LLStrider<LLVector2> mTexcoordsp;
LLStrider<LLColor4U> mColorsp;
std::array<LLTexUnit, LL_NUM_TEXTURE_LAYERS> mTexUnits;

View File

@ -446,15 +446,13 @@ void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 width, S32 height, LLTex
ui_translation.mV[VX] + width * ui_scale.mV[VX],
ui_translation.mV[VY]);
LLGLSUIDefault gls_ui;
gGL.getTexUnit(0)->bind(image, true);
gGL.color4fv(color.mV);
constexpr S32 NUM_VERTICES = 9 * 2 * 3; // 9 quads, 2 triangles per quad, 3 vertices per triangle
static thread_local LLVector2 uv[NUM_VERTICES];
static thread_local LLVector3 pos[NUM_VERTICES];
static thread_local LLVector4a pos[NUM_VERTICES];
S32 index = 0;
@ -719,8 +717,6 @@ void gl_draw_scaled_rotated_image(S32 x, S32 y, S32 width, S32 height, F32 degre
return;
}
LLGLSUIDefault gls_ui;
if(image != NULL)
{
gGL.getTexUnit(0)->bind(image, true);
@ -735,8 +731,8 @@ void gl_draw_scaled_rotated_image(S32 x, S32 y, S32 width, S32 height, F32 degre
if (degrees == 0.f)
{
constexpr S32 NUM_VERTICES = 2 * 3;
static thread_local LLVector2 uv[NUM_VERTICES];
static thread_local LLVector3 pos[NUM_VERTICES];
static thread_local LLVector2 uv[NUM_VERTICES +1];
static thread_local LLVector4a pos[NUM_VERTICES +1];
gGL.begin(LLRender::TRIANGLES);
{

View File

@ -204,11 +204,11 @@ void renderBadgeBackground(F32 centerX, F32 centerY, F32 width, F32 height, cons
(F32)ll_round(x) + width,
(F32)ll_round(y) + height);
LLVector3 vertices[4];
vertices[0] = LLVector3(screen_rect.mLeft, screen_rect.mTop, 1.0f);
vertices[1] = LLVector3(screen_rect.mRight, screen_rect.mTop, 1.0f);
vertices[2] = LLVector3(screen_rect.mLeft, screen_rect.mBottom, 1.0f);
vertices[3] = LLVector3(screen_rect.mRight, screen_rect.mBottom, 1.0f);
LLVector4a vertices[4];
vertices[0].set(screen_rect.mLeft, screen_rect.mTop, 1.0f);
vertices[1].set(screen_rect.mRight, screen_rect.mTop, 1.0f);
vertices[2].set(screen_rect.mLeft, screen_rect.mBottom, 1.0f);
vertices[3].set(screen_rect.mRight, screen_rect.mBottom, 1.0f);
gGL.begin(LLRender::TRIANGLE_STRIP);
{

View File

@ -1632,9 +1632,11 @@ const S32 max_format = (S32)num_formats - 1;
}
else
{
LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBVideoDrvErr"));
// mWindowHandle is 0, going to crash either way
LL_ERRS("Window") << "No wgl_ARB_pixel_format extension!" << LL_ENDL;
LL_WARNS("Window") << "No wgl_ARB_pixel_format extension!" << LL_ENDL;
// cannot proceed without wgl_ARB_pixel_format extension, shutdown same as any other gGLManager.initGL() failure
OSMessageBox(mCallbacks->translateString("MBVideoDrvErr"), mCallbacks->translateString("MBError"), OSMB_OK);
close();
return false;
}
// Verify what pixel format we actually received.

View File

@ -1 +1 @@
7.1.11
7.1.12

View File

@ -3938,9 +3938,9 @@ LLSD LLAppViewer::getViewerInfo() const
LLVector3d pos = gAgent.getPositionGlobal();
info["POSITION"] = ll_sd_from_vector3d(pos);
info["POSITION_LOCAL"] = ll_sd_from_vector3(gAgent.getPosAgentFromGlobal(pos));
info["REGION"] = gAgent.getRegion()->getName();
info["REGION"] = region->getName();
boost::regex regex("\\.(secondlife|lindenlab)\\..*");
info["HOSTNAME"] = boost::regex_replace(gAgent.getRegion()->getSimHostName(), regex, "");
info["HOSTNAME"] = boost::regex_replace(region->getSimHostName(), regex, "");
LLSLURL slurl;
LLAgentUI::buildSLURL(slurl);
info["SLURL"] = slurl.getSLURLString();

View File

@ -398,7 +398,7 @@ F32 logExceptionBenchmark()
__except (msc_exception_filter(GetExceptionCode(), GetExceptionInformation()))
{
// HACK - ensure that profiling is disabled
LLGLSLShader::finishProfile(false);
LLGLSLShader::finishProfile();
// convert to C++ styled exception
char integer_string[32];

View File

@ -1500,7 +1500,8 @@ bool LLFloaterSnapshotBase::ImplBase::updatePreviewList(bool initialized)
void LLFloaterSnapshotBase::ImplBase::updateLivePreview()
{
if (ImplBase::updatePreviewList(true) && mFloater)
// don't update preview for hidden floater
if (mFloater && mFloater->isInVisibleChain() && ImplBase::updatePreviewList(true))
{
LL_DEBUGS() << "changed" << LL_ENDL;
updateControls(mFloater);

View File

@ -941,7 +941,7 @@ struct ShaderProfileHelper
}
~ShaderProfileHelper()
{
LLGLSLShader::finishProfile(false);
LLGLSLShader::finishProfile();
}
};

View File

@ -727,18 +727,25 @@ bool LLSnapshotLivePreview::onIdle( void* snapshot_preview )
return false;
}
static LLCachedControl<bool> auto_snapshot(gSavedSettings, "AutoSnapshot", false);
static LLCachedControl<bool> freeze_time(gSavedSettings, "FreezeTime", false);
static LLCachedControl<bool> use_freeze_frame(gSavedSettings, "UseFreezeFrame", false);
static LLCachedControl<bool> render_ui(gSavedSettings, "RenderUIInSnapshot", false);
static LLCachedControl<bool> render_hud(gSavedSettings, "RenderHUDInSnapshot", false);
static LLCachedControl<bool> render_no_post(gSavedSettings, "RenderSnapshotNoPost", false);
// If we're in freeze-frame and/or auto update mode and camera has moved, update snapshot.
LLVector3 new_camera_pos = LLViewerCamera::getInstance()->getOrigin();
LLQuaternion new_camera_rot = LLViewerCamera::getInstance()->getQuaternion();
if (previewp->mForceUpdateSnapshot ||
(((gSavedSettings.getBOOL("AutoSnapshot") && LLView::isAvailable(previewp->mViewContainer)) ||
(gSavedSettings.getBOOL("FreezeTime") && previewp->mAllowFullScreenPreview)) &&
(((auto_snapshot && LLView::isAvailable(previewp->mViewContainer)) ||
(freeze_time && previewp->mAllowFullScreenPreview)) &&
(new_camera_pos != previewp->mCameraPos || dot(new_camera_rot, previewp->mCameraRot) < 0.995f)))
{
previewp->mCameraPos = new_camera_pos;
previewp->mCameraRot = new_camera_rot;
// request a new snapshot whenever the camera moves, with a time delay
bool new_snapshot = gSavedSettings.getBOOL("AutoSnapshot") || previewp->mForceUpdateSnapshot;
bool new_snapshot = auto_snapshot || previewp->mForceUpdateSnapshot;
LL_DEBUGS("Snapshot") << "camera moved, updating thumbnail" << LL_ENDL;
previewp->updateSnapshot(
new_snapshot, // whether a new snapshot is needed or merely invalidate the existing one
@ -776,10 +783,10 @@ bool LLSnapshotLivePreview::onIdle( void* snapshot_preview )
previewp->getHeight(),
previewp->mKeepAspectRatio,//gSavedSettings.getBOOL("KeepAspectForSnapshot"),
previewp->getSnapshotType() == LLSnapshotModel::SNAPSHOT_TEXTURE,
previewp->mAllowRenderUI && gSavedSettings.getBOOL("RenderUIInSnapshot"),
gSavedSettings.getBOOL("RenderHUDInSnapshot"),
previewp->mAllowRenderUI && render_ui,
render_hud,
false,
gSavedSettings.getBOOL("RenderSnapshotNoPost"),
render_no_post,
previewp->mSnapshotBufferType,
previewp->getMaxImageSize()))
{
@ -791,7 +798,7 @@ bool LLSnapshotLivePreview::onIdle( void* snapshot_preview )
previewp->estimateDataSize();
// Full size preview is set: get the decoded image result and save it for animation
if (gSavedSettings.getBOOL("UseFreezeFrame") && previewp->mAllowFullScreenPreview)
if (use_freeze_frame && previewp->mAllowFullScreenPreview)
{
previewp->prepareFreezeFrame();
}
@ -804,7 +811,7 @@ bool LLSnapshotLivePreview::onIdle( void* snapshot_preview )
previewp->generateThumbnailImage(true) ;
}
previewp->getWindow()->decBusyCount();
previewp->setVisible(gSavedSettings.getBOOL("UseFreezeFrame") && previewp->mAllowFullScreenPreview); // only show fullscreen preview when in freeze frame mode
previewp->setVisible(use_freeze_frame && previewp->mAllowFullScreenPreview); // only show fullscreen preview when in freeze frame mode
previewp->mSnapshotActive = false;
LL_DEBUGS("Snapshot") << "done creating snapshot" << LL_ENDL;
}
@ -924,7 +931,9 @@ LLPointer<LLImageRaw> LLSnapshotLivePreview::getEncodedImage()
else
{
// Update mFormattedImage if necessary
getFormattedImage();
lock.unlock();
getFormattedImage(); // will apply filters to mPreviewImage with a lock
lock.lock();
if (getSnapshotFormat() == LLSnapshotModel::SNAPSHOT_FORMAT_BMP)
{
// BMP hack : copy instead of decode otherwise decode will crash.

View File

@ -28,58 +28,66 @@
#include "llviewerdisplay.h"
#include "llgl.h"
#include "llrender.h"
#include "llglheaders.h"
#include "llgltfmateriallist.h"
#include "fsyspath.h"
#include "hexdump.h"
#include "llagent.h"
#include "llagentcamera.h"
#include "llviewercontrol.h"
#include "llappviewer.h"
#include "llcoord.h"
#include "llcriticaldamp.h"
#include "llcubemap.h"
#include "lldir.h"
#include "lldynamictexture.h"
#include "lldrawpoolalpha.h"
#include "lldrawpoolbump.h"
#include "lldrawpoolwater.h"
#include "lldynamictexture.h"
#include "llenvironment.h"
#include "llfasttimer.h"
#include "llfeaturemanager.h"
//#include "llfirstuse.h"
#include "llfloatertools.h"
#include "llfocusmgr.h"
#include "llgl.h"
#include "llglheaders.h"
#include "llgltfmateriallist.h"
#include "llhudmanager.h"
#include "llimagepng.h"
#include "llmachineid.h"
#include "llmemory.h"
#include "llparcel.h"
#include "llperfstats.h"
#include "llpostprocess.h"
#include "llrender.h"
#include "llscenemonitor.h"
#include "llsdjson.h"
#include "llselectmgr.h"
#include "llsky.h"
#include "llspatialpartition.h"
#include "llstartup.h"
#include "llstartup.h"
#include "lltooldraganddrop.h"
#include "lltoolfocus.h"
#include "lltoolmgr.h"
#include "lltooldraganddrop.h"
#include "lltoolpie.h"
#include "lltracker.h"
#include "lltrans.h"
#include "llui.h"
#include "lluuid.h"
#include "llversioninfo.h"
#include "llviewercamera.h"
#include "llviewercontrol.h"
#include "llviewernetwork.h"
#include "llviewerobjectlist.h"
#include "llviewerparcelmgr.h"
#include "llviewerregion.h"
#include "llviewershadermgr.h"
#include "llviewertexturelist.h"
#include "llviewerwindow.h"
#include "llvoavatarself.h"
#include "llvograss.h"
#include "llworld.h"
#include "pipeline.h"
#include "llspatialpartition.h"
#include "llappviewer.h"
#include "llstartup.h"
#include "llviewershadermgr.h"
#include "llfasttimer.h"
#include "llfloatertools.h"
#include "llviewertexturelist.h"
#include "llfocusmgr.h"
#include "llcubemap.h"
#include "llviewerregion.h"
#include "lldrawpoolwater.h"
#include "lldrawpoolbump.h"
#include "llpostprocess.h"
#include "llscenemonitor.h"
#include "llenvironment.h"
#include "llperfstats.h"
#include <boost/json.hpp>
// [RLVa:KB] - Checked: 2011-05-22 (RLVa-1.3.1a)
#include "llvisualeffect.h"
#include "rlvactions.h"
@ -88,6 +96,10 @@
#include "llpresetsmanager.h"
#include "fsdata.h"
#include <filesystem>
#include <iomanip>
#include <sstream>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
@ -139,6 +151,9 @@ void render_ui_3d();
void render_ui_2d();
void render_disconnected_background();
void getProfileStatsContext(boost::json::object& stats);
std::string getProfileStatsFilename();
void display_startup()
{
if ( !gViewerWindow
@ -1145,10 +1160,89 @@ void display(bool rebuild, F32 zoom_factor, int subfield, bool for_snapshot)
if (gShaderProfileFrame)
{
gShaderProfileFrame = false;
LLGLSLShader::finishProfile();
boost::json::value stats{ boost::json::object_kind };
getProfileStatsContext(stats.as_object());
LLGLSLShader::finishProfile(stats);
auto report_name = getProfileStatsFilename();
std::ofstream outf(report_name);
if (! outf)
{
LL_WARNS() << "Couldn't write to " << std::quoted(report_name) << LL_ENDL;
}
else
{
outf << stats;
LL_INFOS() << "(also dumped to " << std::quoted(report_name) << ")" << LL_ENDL;
}
}
}
void getProfileStatsContext(boost::json::object& stats)
{
// populate the context with info from LLFloaterAbout
auto contextit = stats.emplace("context",
LlsdToJson(LLAppViewer::instance()->getViewerInfo())).first;
auto& context = contextit->value().as_object();
// then add a few more things
unsigned char unique_id[MAC_ADDRESS_BYTES]{};
LLMachineID::getUniqueID(unique_id, sizeof(unique_id));
context.emplace("machine", stringize(LL::hexdump(unique_id, sizeof(unique_id))));
context.emplace("grid", LLGridManager::instance().getGrid());
LLViewerRegion* region = gAgent.getRegion();
if (region)
{
context.emplace("regionid", stringize(region->getRegionID()));
}
LLParcel* parcel = LLViewerParcelMgr::instance().getAgentParcel();
if (parcel)
{
context.emplace("parcel", parcel->getName());
context.emplace("parcelid", parcel->getLocalID());
}
context.emplace("time", LLDate::now().toHTTPDateString("%Y-%m-%dT%H:%M:%S"));
}
std::string getProfileStatsFilename()
{
std::ostringstream basebuff;
// viewer build
basebuff << "profile.v" << LLVersionInfo::instance().getBuild();
// machine ID: zero-initialize unique_id in case LLMachineID fails
unsigned char unique_id[MAC_ADDRESS_BYTES]{};
LLMachineID::getUniqueID(unique_id, sizeof(unique_id));
basebuff << ".m" << LL::hexdump(unique_id, sizeof(unique_id));
// region ID
LLViewerRegion *region = gAgent.getRegion();
basebuff << ".r" << (region? region->getRegionID() : LLUUID());
// local parcel ID
LLParcel* parcel = LLViewerParcelMgr::instance().getAgentParcel();
basebuff << ".p" << (parcel? parcel->getLocalID() : 0);
// date/time -- omit seconds for now
auto now = LLDate::now();
basebuff << ".t" << LLDate::now().toHTTPDateString("%Y-%m-%dT%H-%M-");
// put this candidate file in our logs directory
auto base = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, basebuff.str());
S32 sec;
now.split(nullptr, nullptr, nullptr, nullptr, nullptr, &sec);
// Loop over finished filename, incrementing sec until we find one that
// doesn't yet exist. Should rarely loop (only if successive calls within
// same second), may produce (e.g.) sec==61, but avoids collisions and
// preserves chronological filename sort order.
std::string name;
std::error_code ec;
do
{
// base + missing 2-digit seconds, append ".json"
// post-increment sec in case we have to try again
name = stringize(base, std::setw(2), std::setfill('0'), sec++, ".json");
} while (std::filesystem::exists(fsyspath(name), ec));
// Ignoring ec means we might potentially return a name that does already
// exist -- but if we can't check its existence, what more can we do?
return name;
}
// WIP simplified copy of display() that does minimal work
void display_cube_face()
{

View File

@ -692,22 +692,10 @@ void LLViewerMedia::updateMedia(void *dummy_arg)
static LLCachedControl<bool> inworld_media_enabled(gSavedSettings, "AudioStreamingMedia", true);
static LLCachedControl<bool> inworld_audio_enabled(gSavedSettings, "AudioStreamingMusic", true);
// <FS:Ansariel> Replace frequently called gSavedSettings
//U32 max_instances = gSavedSettings.getU32("PluginInstancesTotal");
//U32 max_normal = gSavedSettings.getU32("PluginInstancesNormal");
//U32 max_low = gSavedSettings.getU32("PluginInstancesLow");
//F32 max_cpu = gSavedSettings.getF32("PluginInstancesCPULimit");
static LLCachedControl<U32> sPluginInstancesTotal(gSavedSettings, "PluginInstancesTotal");
static LLCachedControl<U32> sPluginInstancesNormal(gSavedSettings, "PluginInstancesNormal");
static LLCachedControl<U32> sPluginInstancesLow(gSavedSettings, "PluginInstancesLow");
static LLCachedControl<F32> sPluginInstancesCPULimit(gSavedSettings, "PluginInstancesCPULimit");
U32 max_instances = sPluginInstancesTotal();
U32 max_normal = sPluginInstancesNormal();
U32 max_low = sPluginInstancesLow();
F32 max_cpu = sPluginInstancesCPULimit();
// </FS:Ansariel>
static LLCachedControl<U32> max_instances(gSavedSettings, "PluginInstancesTotal", 8);
static LLCachedControl<U32> max_normal(gSavedSettings, "PluginInstancesNormal", 2);
static LLCachedControl<U32> max_low(gSavedSettings, "PluginInstancesLow", 4);
static LLCachedControl<F32> max_cpu(gSavedSettings, "PluginInstancesCPULimit", 0.9);
// Setting max_cpu to 0.0 disables CPU usage checking.
bool check_cpu_usage = (max_cpu != 0.0f);
@ -845,7 +833,8 @@ void LLViewerMedia::updateMedia(void *dummy_arg)
}
else
{
if(gAudiop && LLViewerMedia::hasParcelAudio() && restore_parcel_audio && gSavedSettings.getBOOL("MediaTentativeAutoPlay"))
static LLCachedControl<bool> auto_play(gSavedSettings, "MediaTentativeAutoPlay", true);
if(gAudiop && LLViewerMedia::hasParcelAudio() && restore_parcel_audio && auto_play())
{
LLViewerAudio::getInstance()->startInternetStreamWithAutoFade(LLViewerMedia::getParcelAudioURL());
restore_parcel_audio = false;
@ -896,11 +885,8 @@ void LLViewerMedia::updateMedia(void *dummy_arg)
}
}
// <FS:Ansariel> Replace frequently called gSavedSettings
//if(gSavedSettings.getBOOL("MediaPerformanceManagerDebug"))
static LLCachedControl<bool> sMediaPerformanceManagerDebug(gSavedSettings, "MediaPerformanceManagerDebug");
if(sMediaPerformanceManagerDebug)
// </FS:Ansariel>
static LLCachedControl<bool> perf_debug(gSavedSettings, "MediaPerformanceManagerDebug", false);
if(perf_debug())
{
// Give impls the same ordering as the priority list
// they're already in the right order for this.

View File

@ -87,6 +87,8 @@ namespace {
const F32 SPEAKING_AUDIO_LEVEL = 0.30;
const uint32_t PEER_GAIN_CONVERSION_FACTOR = 220;
static const std::string REPORTED_VOICE_SERVER_TYPE = "Secondlife WebRTC Gateway";
// Don't send positional updates more frequently than this:
@ -2443,7 +2445,7 @@ void LLVoiceWebRTCConnection::setSpeakerVolume(F32 volume)
void LLVoiceWebRTCConnection::setUserVolume(const LLUUID& id, F32 volume)
{
boost::json::object root = {{"ug", {id.asString(), (uint32_t) (volume * 200)}}};
boost::json::object root = { { "ug", { { id.asString(), (uint32_t)(volume * PEER_GAIN_CONVERSION_FACTOR) } } } };
std::string json_data = boost::json::serialize(root);
if (mWebRTCDataInterface)
{
@ -2453,7 +2455,7 @@ void LLVoiceWebRTCConnection::setUserVolume(const LLUUID& id, F32 volume)
void LLVoiceWebRTCConnection::setUserMute(const LLUUID& id, bool mute)
{
boost::json::object root = {{"m", {id.asString(), mute}}};
boost::json::object root = { { "m", { { id.asString(), mute } } } };
std::string json_data = boost::json::serialize(root);
if (mWebRTCDataInterface)
{

46
scripts/perf/logsdir.py Normal file
View File

@ -0,0 +1,46 @@
#!/usr/bin/env python3
"""\
@file logsdir.py
@author Nat Goodspeed
@date 2024-09-12
@brief Locate the Second Life logs directory for the current user on the
current platform.
$LicenseInfo:firstyear=2024&license=viewerlgpl$
Copyright (c) 2024, Linden Research, Inc.
$/LicenseInfo$
"""
import os
from pathlib import Path
import platform
class Error(Exception):
pass
# logic used by SLVersionChecker
def logsdir():
app = 'SecondLife'
system = platform.system()
if (system == 'Darwin'):
base_dir = os.path.join(os.path.expanduser('~'),
'Library','Application Support',app)
elif (system == 'Linux'):
base_dir = os.path.join(os.path.expanduser('~'),
'.' + app.lower())
elif (system == 'Windows'):
appdata = os.getenv('APPDATA')
base_dir = os.path.join(appdata, app)
else:
raise ValueError("Unsupported platform '%s'" % system)
return os.path.join(base_dir, 'logs')
def latest_file(dirpath, pattern):
files = Path(dirpath).glob(pattern)
sort = [(p.stat().st_mtime, p) for p in files if p.is_file()]
sort.sort(reverse=True)
try:
return sort[0][1]
except IndexError:
raise Error(f'No {pattern} files in {dirpath}')

104
scripts/perf/profile_cmp.py Normal file
View File

@ -0,0 +1,104 @@
#!/usr/bin/env python3
"""\
@file profile_cmp.py
@author Nat Goodspeed
@date 2024-09-13
@brief Compare a frame profile stats file with a similar baseline file.
$LicenseInfo:firstyear=2024&license=viewerlgpl$
Copyright (c) 2024, Linden Research, Inc.
$/LicenseInfo$
"""
from datetime import datetime
import json
from logsdir import Error, latest_file, logsdir
from pathlib import Path
import sys
# variance that's ignorable
DEFAULT_EPSILON = 0.03 # 3%
def compare(baseline, test, epsilon=DEFAULT_EPSILON):
if Path(baseline).samefile(test):
print(f'{baseline} same as\n{test}\nAnalysis moot.')
return
with open(baseline) as inf:
bdata = json.load(inf)
with open(test) as inf:
tdata = json.load(inf)
print(f'baseline {baseline}\ntestfile {test}')
for k, tv in tdata['context'].items():
bv = bdata['context'].get(k)
if bv != tv:
print(f'baseline {k}={bv} vs.\ntestfile {k}={tv}')
btime = bdata['context'].get('time')
ttime = tdata['context'].get('time')
if btime and ttime:
print('testfile newer by',
datetime.fromisoformat(ttime) - datetime.fromisoformat(btime))
# The following ignores totals and unused shaders, except to the extent
# that some shaders were used in the baseline but not in the recent test
# or vice-versa. While the viewer considers that a shader has been used if
# 'binds' is nonzero, we exclude any whose 'time' is zero to avoid zero
# division.
bshaders = {s['name']: s for s in bdata['shaders'] if s['time'] and s['samples']}
tshaders = {s['name']: s for s in tdata['shaders'] if s['time']}
bothshaders = set(bshaders).intersection(tshaders)
deltas = []
for shader in bothshaders:
bshader = bshaders[shader]
tshader = tshaders[shader]
bthruput = bshader['samples']/bshader['time']
tthruput = tshader['samples']/tshader['time']
delta = (tthruput - bthruput)/bthruput
if abs(delta) > epsilon:
deltas.append((delta, shader, bthruput, tthruput))
# descending order of performance gain
deltas.sort(reverse=True)
print(f'{len(deltas)} shaders showed nontrivial performance differences '
'(millon samples/sec):')
namelen = max(len(s[1]) for s in deltas) if deltas else 0
for delta, shader, bthruput, tthruput in deltas:
print(f' {shader.rjust(namelen)} {delta*100:6.1f}% '
f'{bthruput/1000000:8.2f} -> {tthruput/1000000:8.2f}')
tunused = set(bshaders).difference(tshaders)
print(f'{len(tunused)} baseline shaders not used in test:')
for s in tunused:
print(f' {s}')
bunused = set(tshaders).difference(bshaders)
print(f'{len(bunused)} shaders newly used in test:')
for s in bunused:
print(f' {s}')
def main(*raw_args):
from argparse import ArgumentParser
parser = ArgumentParser(description="""
%(prog)s compares a baseline JSON file from Develop -> Render Tests -> Frame
Profile to another such file from a more recent test. It identifies shaders
that have gained and lost in throughput.
""")
parser.add_argument('-e', '--epsilon', type=float, default=int(DEFAULT_EPSILON*100),
help="""percent variance considered ignorable (default %(default)s%%)""")
parser.add_argument('baseline',
help="""baseline profile filename to compare against""")
parser.add_argument('test', nargs='?',
help="""test profile filename to compare
(default is most recent)""")
args = parser.parse_args(raw_args)
compare(args.baseline,
args.test or latest_file(logsdir(), 'profile.*.json'),
epsilon=(args.epsilon / 100.))
if __name__ == "__main__":
try:
sys.exit(main(*sys.argv[1:]))
except (Error, OSError, json.JSONDecodeError) as err:
sys.exit(str(err))

View File

@ -0,0 +1,60 @@
#!/usr/bin/env python3
"""\
@file profile_csv.py
@author Nat Goodspeed
@date 2024-09-12
@brief Convert a JSON file from Develop -> Render Tests -> Frame Profile to CSV
$LicenseInfo:firstyear=2024&license=viewerlgpl$
Copyright (c) 2024, Linden Research, Inc.
$/LicenseInfo$
"""
import json
from logsdir import Error, latest_file, logsdir
import sys
def convert(path, totals=True, unused=True, file=sys.stdout):
with open(path) as inf:
data = json.load(inf)
# print path to sys.stderr in case user is redirecting stdout
print(path, file=sys.stderr)
print('"name", "file1", "file2", "time", "binds", "samples", "triangles"', file=file)
if totals:
t = data['totals']
print(f'"totals", "", "", {t["time"]}, {t["binds"]}, {t["samples"]}, {t["triangles"]}',
file=file)
for sh in data['shaders']:
print(f'"{sh["name"]}", "{sh["files"][0]}", "{sh["files"][1]}", '
f'{sh["time"]}, {sh["binds"]}, {sh["samples"]}, {sh["triangles"]}', file=file)
if unused:
for u in data['unused']:
print(f'"{u}", "", "", 0, 0, 0, 0', file=file)
def main(*raw_args):
from argparse import ArgumentParser
parser = ArgumentParser(description="""
%(prog)s converts a JSON file from Develop -> Render Tests -> Frame Profile to
a more-or-less equivalent CSV file. It expands the totals stats and unused
shaders list to full shaders lines.
""")
parser.add_argument('-t', '--totals', action='store_false', default=True,
help="""omit totals from CSV file""")
parser.add_argument('-u', '--unused', action='store_false', default=True,
help="""omit unused shaders from CSV file""")
parser.add_argument('path', nargs='?',
help="""profile filename to convert (default is most recent)""")
args = parser.parse_args(raw_args)
convert(args.path or latest_file(logsdir(), 'profile.*.json'),
totals=args.totals, unused=args.unused)
if __name__ == "__main__":
try:
sys.exit(main(*sys.argv[1:]))
except (Error, OSError, json.JSONDecodeError) as err:
sys.exit(str(err))

View File

@ -0,0 +1,40 @@
#!/usr/bin/env python3
"""\
@file profile_pretty.py
@author Nat Goodspeed
@date 2024-09-12
@brief Pretty-print a JSON file from Develop -> Render Tests -> Frame Profile
$LicenseInfo:firstyear=2024&license=viewerlgpl$
Copyright (c) 2024, Linden Research, Inc.
$/LicenseInfo$
"""
import json
from logsdir import Error, latest_file, logsdir
import sys
def pretty(path):
with open(path) as inf:
data = json.load(inf)
# print path to sys.stderr in case user is redirecting stdout
print(path, file=sys.stderr)
json.dump(data, sys.stdout, indent=4)
def main(*raw_args):
from argparse import ArgumentParser
parser = ArgumentParser(description="""
%(prog)s pretty-prints a JSON file from Develop -> Render Tests -> Frame Profile.
The file produced by the viewer is a single dense line of JSON.
""")
parser.add_argument('path', nargs='?',
help="""profile filename to pretty-print (default is most recent)""")
args = parser.parse_args(raw_args)
pretty(args.path or latest_file(logsdir(), 'profile.*.json'))
if __name__ == "__main__":
try:
sys.exit(main(*sys.argv[1:]))
except (Error, OSError, json.JSONDecodeError) as err:
sys.exit(str(err))