summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xindra/llcommon/hexdump.h4
-rw-r--r--indra/llcommon/llinstancetracker.h1
-rw-r--r--indra/llcommon/llleaplistener.cpp2
-rw-r--r--indra/llcommon/llleaplistener.h2
-rw-r--r--indra/llcommon/llstring.cpp131
-rw-r--r--indra/llcommon/llstring.h6
-rw-r--r--indra/llcommon/lua_function.cpp12
-rw-r--r--indra/llcommon/lua_function.h13
-rw-r--r--indra/llcommon/tests/llrand_test.cpp2
-rw-r--r--indra/llrender/llglslshader.cpp121
-rw-r--r--indra/llrender/llglslshader.h10
-rw-r--r--indra/llui/llviewereventrecorder.cpp11
-rw-r--r--indra/newview/llappviewerlistener.cpp13
-rw-r--r--indra/newview/llfeaturemanager.cpp2
-rw-r--r--indra/newview/llglsandbox.cpp2
-rw-r--r--indra/newview/llluamanager.cpp2
-rw-r--r--indra/newview/llpanelemojicomplete.cpp3
-rw-r--r--indra/newview/llurldispatcher.cpp9
-rw-r--r--indra/newview/llviewerdisplay.cpp102
-rw-r--r--indra/newview/llviewermedia.cpp3
-rw-r--r--indra/newview/scripts/lua/frame_profile.lua24
-rw-r--r--indra/newview/scripts/lua/frame_profile_quit.lua25
-rw-r--r--indra/newview/scripts/lua/require/LLAgent.lua9
-rw-r--r--indra/newview/scripts/lua/test_animation.lua28
-rw-r--r--indra/newview/tests/llluamanager_test.cpp2
-rwxr-xr-xindra/newview/viewer_manifest.py6
-rw-r--r--indra/test/namedtempfile.h24
-rwxr-xr-xscripts/perf/frame_profile10
-rw-r--r--scripts/perf/profile_csv.py72
-rw-r--r--scripts/perf/profile_pretty.py54
30 files changed, 521 insertions, 184 deletions
diff --git a/indra/llcommon/hexdump.h b/indra/llcommon/hexdump.h
index ab5ba2b16d..4b734426a3 100755
--- a/indra/llcommon/hexdump.h
+++ b/indra/llcommon/hexdump.h
@@ -25,7 +25,7 @@ namespace LL
class hexdump
{
public:
- hexdump(const std::string_view& data):
+ hexdump(std::string_view data):
hexdump(data.data(), data.length())
{}
@@ -66,7 +66,7 @@ private:
class hexmix
{
public:
- hexmix(const std::string_view& data):
+ hexmix(std::string_view data):
mData(data)
{}
diff --git a/indra/llcommon/llinstancetracker.h b/indra/llcommon/llinstancetracker.h
index 03418e9bad..b5c681e60a 100644
--- a/indra/llcommon/llinstancetracker.h
+++ b/indra/llcommon/llinstancetracker.h
@@ -41,6 +41,7 @@
#include <boost/iterator/indirect_iterator.hpp>
#include <boost/iterator/filter_iterator.hpp>
+#include "llprofiler.h"
#include "lockstatic.h"
#include "stringize.h"
diff --git a/indra/llcommon/llleaplistener.cpp b/indra/llcommon/llleaplistener.cpp
index b81ee66ba9..9742c9e9de 100644
--- a/indra/llcommon/llleaplistener.cpp
+++ b/indra/llcommon/llleaplistener.cpp
@@ -54,7 +54,7 @@
return features;
}
-LLLeapListener::LLLeapListener(const std::string_view& caller, const Callback& callback):
+LLLeapListener::LLLeapListener(std::string_view caller, const Callback& callback):
// Each LEAP plugin has an instance of this listener. Make the command
// pump name difficult for other such plugins to guess.
LLEventAPI(LLUUID::generateNewID().asString(),
diff --git a/indra/llcommon/llleaplistener.h b/indra/llcommon/llleaplistener.h
index d36d2ff8db..d38a6f4ace 100644
--- a/indra/llcommon/llleaplistener.h
+++ b/indra/llcommon/llleaplistener.h
@@ -28,7 +28,7 @@ public:
* event is received.
*/
using Callback = std::function<bool(const std::string& pump, const LLSD& data)>;
- LLLeapListener(const std::string_view& caller, const Callback& callback);
+ LLLeapListener(std::string_view caller, const Callback& callback);
~LLLeapListener();
LLEventPump& getReplyPump() { return mReplyPump; }
diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp
index 505789f9ea..005864a843 100644
--- a/indra/llcommon/llstring.cpp
+++ b/indra/llcommon/llstring.cpp
@@ -31,6 +31,7 @@
#include "llfasttimer.h"
#include "llsd.h"
#include <vector>
+#include <sstream>
#if LL_WINDOWS
#include "llwin32headerslean.h"
@@ -141,10 +142,10 @@ std::string rawstr_to_utf8(const std::string& raw)
return wstring_to_utf8str(wstr);
}
-std::ptrdiff_t wchar_to_utf8chars(llwchar in_char, char* outchars)
+std::string wchar_to_utf8chars(llwchar in_char)
{
- U32 cur_char = (U32)in_char;
- char* base = outchars;
+ U32 cur_char(in_char);
+ char buff[8], *outchars = buff;
if (cur_char < 0x80)
{
*outchars++ = (U8)cur_char;
@@ -189,7 +190,7 @@ std::ptrdiff_t wchar_to_utf8chars(llwchar in_char, char* outchars)
LL_WARNS() << "Invalid Unicode character " << cur_char << "!" << LL_ENDL;
*outchars++ = LL_UNKNOWN_CHAR;
}
- return outchars - base;
+ return { buff, std::string::size_type(outchars - buff) };
}
auto utf16chars_to_wchar(const U16* inchars, llwchar* outchar)
@@ -214,7 +215,8 @@ auto utf16chars_to_wchar(const U16* inchars, llwchar* outchar)
llutf16string wstring_to_utf16str(const llwchar* utf32str, size_t len)
{
- llutf16string out;
+ // ostringstream for llutf16string
+ std::basic_ostringstream<U16> out;
S32 i = 0;
while (i < len)
@@ -222,16 +224,16 @@ llutf16string wstring_to_utf16str(const llwchar* utf32str, size_t len)
U32 cur_char = utf32str[i];
if (cur_char > 0xFFFF)
{
- out += (0xD7C0 + (cur_char >> 10));
- out += (0xDC00 | (cur_char & 0x3FF));
+ out.put(U16(0xD7C0 + (cur_char >> 10)));
+ out.put(U16(0xDC00 | (cur_char & 0x3FF)));
}
else
{
- out += cur_char;
+ out.put(U16(cur_char));
}
i++;
}
- return out;
+ return out.str();
}
llutf16string utf8str_to_utf16str( const char* utf8str, size_t len )
@@ -242,8 +244,16 @@ llutf16string utf8str_to_utf16str( const char* utf8str, size_t len )
LLWString utf16str_to_wstring(const U16* utf16str, size_t len)
{
- LLWString wout;
- if (len == 0) return wout;
+ if (len == 0) return {};
+
+ // MS doesn't support std::basic_ostringstream<llwchar>; have to work
+ // around it.
+ std::vector<llwchar> wout;
+ // We want to minimize allocations. We don't know how many llwchars we'll
+ // generate from this utf16str, but we do know the length should be at
+ // most len. So if we reserve 'len' llwchars, we shouldn't need to expand
+ // wout incrementally.
+ wout.reserve(len);
S32 i = 0;
const U16* chars16 = utf16str;
@@ -251,9 +261,9 @@ LLWString utf16str_to_wstring(const U16* utf16str, size_t len)
{
llwchar cur_char;
i += (S32)utf16chars_to_wchar(chars16+i, &cur_char);
- wout += cur_char;
+ wout.push_back(cur_char);
}
- return wout;
+ return { wout.begin(), wout.end() };
}
// Length in llwchar (UTF-32) of the first len units (16 bits) of the given UTF-16 string.
@@ -367,13 +377,12 @@ std::string wchar_utf8_preview(const llwchar wc)
std::ostringstream oss;
oss << std::hex << std::uppercase << (U32)wc;
- U8 out_bytes[8];
- U32 size = (U32)wchar_to_utf8chars(wc, (char*)out_bytes);
+ auto out_bytes = wchar_to_utf8chars(wc);
- if (size > 1)
+ if (out_bytes.length() > 1)
{
oss << " [";
- for (U32 i = 0; i < size; ++i)
+ for (U32 i = 0; i < out_bytes.length(); ++i)
{
if (i)
{
@@ -399,7 +408,14 @@ S32 wstring_utf8_length(const LLWString& wstr)
LLWString utf8str_to_wstring(const char* utf8str, size_t len)
{
- LLWString wout;
+ // MS doesn't support std::basic_ostringstream<llwchar>; have to work
+ // around it.
+ std::vector<llwchar> wout;
+ // We want to minimize allocations. We don't know how many llwchars we'll
+ // generate from this utf8str, but we do know the length should be at most
+ // len. So if we reserve 'len' llwchars, we shouldn't need to expand wout
+ // incrementally.
+ wout.reserve(len);
S32 i = 0;
while (i < len)
@@ -442,7 +458,7 @@ LLWString utf8str_to_wstring(const char* utf8str, size_t len)
}
else
{
- wout += LL_UNKNOWN_CHAR;
+ wout.push_back(LL_UNKNOWN_CHAR);
++i;
continue;
}
@@ -479,26 +495,21 @@ LLWString utf8str_to_wstring(const char* utf8str, size_t len)
}
}
- wout += unichar;
+ wout.push_back(unichar);
++i;
}
- return wout;
+ return { wout.begin(), wout.end() };
}
std::string wstring_to_utf8str(const llwchar* utf32str, size_t len)
{
- std::string out;
+ std::ostringstream out;
- S32 i = 0;
- while (i < len)
+ for (size_t i = 0; i < len; ++i)
{
- char tchars[8]; /* Flawfinder: ignore */
- auto n = wchar_to_utf8chars(utf32str[i], tchars);
- tchars[n] = 0;
- out += tchars;
- i++;
+ out << wchar_to_utf8chars(utf32str[i]);
}
- return out;
+ return out.str();
}
std::string utf16str_to_utf8str(const U16* utf16str, size_t len)
@@ -686,7 +697,21 @@ llwchar utf8str_to_wchar(const std::string& utf8str, size_t offset, size_t lengt
std::string utf8str_showBytesUTF8(const std::string& utf8str)
{
- std::string result;
+ std::ostringstream result;
+ char lastchar = '\0';
+ auto append = [&result, &lastchar](char c)
+ {
+ lastchar = c;
+ result << c;
+ };
+ auto appends = [&result, &lastchar](const std::string& s)
+ {
+ if (! s.empty())
+ {
+ lastchar = s.back();
+ result << s;
+ }
+ };
bool in_sequence = false;
size_t sequence_size = 0;
@@ -695,9 +720,9 @@ std::string utf8str_showBytesUTF8(const std::string& utf8str)
auto open_sequence = [&]()
{
- if (!result.empty() && result.back() != '\n')
- result += '\n'; // Use LF as a separator before new UTF-8 sequence
- result += '[';
+ if (lastchar != '\0' && lastchar != '\n')
+ append('\n'); // Use LF as a separator before new UTF-8 sequence
+ append('[');
in_sequence = true;
};
@@ -706,9 +731,9 @@ std::string utf8str_showBytesUTF8(const std::string& utf8str)
llwchar unicode = utf8str_to_wchar(utf8str, byte_index - sequence_size, sequence_size);
if (unicode != LL_UNKNOWN_CHAR)
{
- result += llformat("+%04X", unicode);
+ appends(llformat("+%04X", unicode));
}
- result += ']';
+ append(']');
in_sequence = false;
sequence_size = 0;
};
@@ -729,9 +754,9 @@ std::string utf8str_showBytesUTF8(const std::string& utf8str)
}
else // Continue the same UTF-8 sequence
{
- result += '.';
+ append('.');
}
- result += llformat("%02X", byte); // The byte is represented in hexadecimal form
+ appends(llformat("%02X", byte)); // The byte is represented in hexadecimal form
++sequence_size;
}
else // ASCII symbol is represented as a character
@@ -741,10 +766,10 @@ std::string utf8str_showBytesUTF8(const std::string& utf8str)
close_sequence();
if (byte != '\n')
{
- result += '\n'; // Use LF as a separator between UTF-8 and ASCII
+ append('\n'); // Use LF as a separator between UTF-8 and ASCII
}
}
- result += byte;
+ append(byte);
}
++byte_index;
}
@@ -754,7 +779,7 @@ std::string utf8str_showBytesUTF8(const std::string& utf8str)
close_sequence();
}
- return result;
+ return result.str();
}
// Search for any emoji symbol, return true if found
@@ -1587,7 +1612,7 @@ S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions)
LL_PROFILE_ZONE_SCOPED_CATEGORY_STRING;
S32 res = 0;
- std::string output;
+ std::ostringstream output;
std::vector<std::string> tokens;
std::string::size_type start = 0;
@@ -1595,7 +1620,7 @@ S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions)
std::string::size_type key_start = 0;
while ((key_start = getSubstitution(s, start, tokens)) != std::string::npos)
{
- output += std::string(s, prev_start, key_start-prev_start);
+ output << std::string(s, prev_start, key_start-prev_start);
prev_start = start;
bool found_replacement = false;
@@ -1636,20 +1661,20 @@ S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions)
if (found_replacement)
{
- output += replacement;
+ output << replacement;
res++;
}
else
{
// we had no replacement, use the string as is
// e.g. "hello [MISSING_REPLACEMENT]" or "-=[Stylized Name]=-"
- output += std::string(s, key_start, start-key_start);
+ output << std::string(s, key_start, start-key_start);
}
tokens.clear();
}
// send the remainder of the string (with no further matches for bracketed names)
- output += std::string(s, start);
- s = output;
+ output << std::string(s, start);
+ s = output.str();
return res;
}
@@ -1665,7 +1690,7 @@ S32 LLStringUtil::format(std::string& s, const LLSD& substitutions)
return res;
}
- std::string output;
+ std::ostringstream output;
std::vector<std::string> tokens;
std::string::size_type start = 0;
@@ -1673,7 +1698,7 @@ S32 LLStringUtil::format(std::string& s, const LLSD& substitutions)
std::string::size_type key_start = 0;
while ((key_start = getSubstitution(s, start, tokens)) != std::string::npos)
{
- output += std::string(s, prev_start, key_start-prev_start);
+ output << std::string(s, prev_start, key_start-prev_start);
prev_start = start;
bool found_replacement = false;
@@ -1706,20 +1731,20 @@ S32 LLStringUtil::format(std::string& s, const LLSD& substitutions)
if (found_replacement)
{
- output += replacement;
+ output << replacement;
res++;
}
else
{
// we had no replacement, use the string as is
// e.g. "hello [MISSING_REPLACEMENT]" or "-=[Stylized Name]=-"
- output += std::string(s, key_start, start-key_start);
+ output << std::string(s, key_start, start-key_start);
}
tokens.clear();
}
// send the remainder of the string (with no further matches for bracketed names)
- output += std::string(s, start);
- s = output;
+ output << std::string(s, start);
+ s = output.str();
return res;
}
diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h
index e4be1efaed..b552aede82 100644
--- a/indra/llcommon/llstring.h
+++ b/indra/llcommon/llstring.h
@@ -707,7 +707,11 @@ ll_convert_forms(ll_convert_alias, LLWString, std::string, utf8str_to_
// Same function, better name. JC
inline LLWString utf8string_to_wstring(const std::string& utf8_string) { return utf8str_to_wstring(utf8_string); }
-LL_COMMON_API std::ptrdiff_t wchar_to_utf8chars(llwchar inchar, char* outchars);
+// return a UTF-8 string representation of a single llwchar, which we
+// occasionally require:
+// cheaper than ll_convert_to<std::string>(LLWString(1, inchar))
+LL_COMMON_API std::string wchar_to_utf8chars(llwchar inchar);
+ll_convert_alias(std::string, llwchar, wchar_to_utf8chars(in));
ll_convert_forms(ll_convert_alias, std::string, LLWString, wstring_to_utf8str);
ll_convert_forms(ll_convert_u16_alias, std::string, llutf16string, utf16str_to_utf8str);
diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp
index eefb1e62cf..2557fd0cc9 100644
--- a/indra/llcommon/lua_function.cpp
+++ b/indra/llcommon/lua_function.cpp
@@ -709,6 +709,11 @@ int lua_metaipair(lua_State* L)
LuaState::~LuaState()
{
+ // If we're unwinding the stack due to an exception, don't bother trying
+ // to call any callbacks -- either Lua or C++.
+ if (std::uncaught_exceptions() != 0)
+ return;
+
/*---------------------------- feature flag ----------------------------*/
if (mFeature)
/*---------------------------- feature flag ----------------------------*/
@@ -990,7 +995,8 @@ lua_function(atexit, "atexit(function): "
*****************************************************************************/
LuaPopper::~LuaPopper()
{
- if (mCount)
+ // If we're unwinding the C++ stack due to an exception, don't pop!
+ if (std::uncaught_exceptions() == 0 && mCount)
{
lua_pop(mState, mCount);
}
@@ -999,8 +1005,8 @@ LuaPopper::~LuaPopper()
/*****************************************************************************
* LuaFunction class
*****************************************************************************/
-LuaFunction::LuaFunction(const std::string_view& name, lua_CFunction function,
- const std::string_view& helptext)
+LuaFunction::LuaFunction(std::string_view name, lua_CFunction function,
+ std::string_view helptext)
{
const auto& [registry, lookup] = getState();
registry.emplace(name, Registry::mapped_type{ function, helptext });
diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h
index 967d8eaba1..10c201c234 100644
--- a/indra/llcommon/lua_function.h
+++ b/indra/llcommon/lua_function.h
@@ -172,7 +172,10 @@ public:
LuaRemover& operator=(const LuaRemover&) = delete;
~LuaRemover()
{
- lua_remove(mState, mIndex);
+ // If we're unwinding the C++ stack due to an exception, don't mess
+ // with the Lua stack!
+ if (std::uncaught_exceptions() == 0)
+ lua_remove(mState, mIndex);
}
private:
@@ -351,7 +354,7 @@ auto lua_setfieldv(lua_State* L, int index, const char* k, const T& value)
// return to C++, from table at index, the value of field k (without metamethods)
template <typename T>
-auto lua_rawgetfield(lua_State* L, int index, const std::string_view& k)
+auto lua_rawgetfield(lua_State* L, int index, std::string_view k)
{
index = lua_absindex(L, index);
lua_checkdelta(L);
@@ -364,7 +367,7 @@ auto lua_rawgetfield(lua_State* L, int index, const std::string_view& k)
// set in table at index, as field k, the specified C++ value (without metamethods)
template <typename T>
-void lua_rawsetfield(lua_State* L, int index, const std::string_view& k, const T& value)
+void lua_rawsetfield(lua_State* L, int index, std::string_view k, const T& value)
{
index = lua_absindex(L, index);
lua_checkdelta(L);
@@ -389,8 +392,8 @@ void lua_rawsetfield(lua_State* L, int index, const std::string_view& k, const T
class LuaFunction
{
public:
- LuaFunction(const std::string_view& name, lua_CFunction function,
- const std::string_view& helptext);
+ LuaFunction(std::string_view name, lua_CFunction function,
+ std::string_view helptext);
static void init(lua_State* L);
diff --git a/indra/llcommon/tests/llrand_test.cpp b/indra/llcommon/tests/llrand_test.cpp
index a0dd4ef576..85dd53ce96 100644
--- a/indra/llcommon/tests/llrand_test.cpp
+++ b/indra/llcommon/tests/llrand_test.cpp
@@ -38,7 +38,7 @@
// testing extent < 0, negate the return value and the extent before passing
// into ensure_in_range().
template <typename NUMBER>
-void ensure_in_range(const std::string_view& name,
+void ensure_in_range(std::string_view name,
NUMBER value, NUMBER low, NUMBER high)
{
auto failmsg{ stringize(name, " >= ", low, " (", value, ')') };
diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp
index a157bfee21..56f1533708 100644
--- a/indra/llrender/llglslshader.cpp
+++ b/indra/llrender/llglslshader.cpp
@@ -41,6 +41,8 @@
#include "OpenGL/OpenGL.h"
#endif
+#include <fstream>
+
// Print-print list of shader included source files that are linked together via glAttachShader()
// i.e. On macOS / OSX the AMD GLSL linker will display an error if a varying is left in an undefined state.
#define DEBUG_SHADER_INCLUDES 0
@@ -63,6 +65,7 @@ U64 LLGLSLShader::sTotalTimeElapsed = 0;
U32 LLGLSLShader::sTotalTrianglesDrawn = 0;
U64 LLGLSLShader::sTotalSamplesDrawn = 0;
U32 LLGLSLShader::sTotalBinds = 0;
+std::string LLGLSLShader::sDefaultReportName;
//UI shader -- declared here so llui_libtest will link properly
LLGLSLShader gUIProgram;
@@ -101,9 +104,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,48 +120,71 @@ struct LLGLSLShaderCompareTimeElapsed
};
//static
-void LLGLSLShader::finishProfile(bool emit_report)
+void LLGLSLShader::finishProfile(const std::string& report_name)
{
sProfileEnabled = false;
- if (emit_report)
+ if (! report_name.empty())
{
- 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());
+ boost::json::object stats;
+ 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);
}
}
}
+
+ 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;
+ }
}
}
@@ -170,36 +196,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;
+ LL_INFOS() << mShaderFiles[i].first << LL_ENDL;
+ files.emplace_back(mShaderFiles[i].first);
+ }
+ LL_INFOS() << "=============================================" << LL_ENDL;
- F32 ms = mTimeElapsed / 1000000.f;
- F32 seconds = ms / 1000.f;
+ 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 / 1000000.0);
- tris_sec /= seconds;
+ 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 / 1000000000.0);
- samples_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;
+ 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() << "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
diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h
index 27c8f0b7d0..a9b9bfafa8 100644
--- a/indra/llrender/llglslshader.h
+++ b/indra/llrender/llglslshader.h
@@ -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(const std::string& report_name=sDefaultReportName);
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 report_name parameter to a
+ // temporary std::string, that temporary must be destroyed when the stack
+ // is unwound, which __try forbids.
+ static std::string sDefaultReportName;
};
//UI shader (declared here so llui_libtest will link properly)
diff --git a/indra/llui/llviewereventrecorder.cpp b/indra/llui/llviewereventrecorder.cpp
index 6d907d7e45..0a4fe5234b 100644
--- a/indra/llui/llviewereventrecorder.cpp
+++ b/indra/llui/llviewereventrecorder.cpp
@@ -24,9 +24,10 @@
*/
-#include "llviewereventrecorder.h"
-#include "llui.h"
#include "llleap.h"
+#include "llstring.h"
+#include "llui.h"
+#include "llviewereventrecorder.h"
LLViewerEventRecorder::LLViewerEventRecorder() {
@@ -247,11 +248,9 @@ void LLViewerEventRecorder::logKeyUnicodeEvent(llwchar uni_char) {
// keycode...or
// char
- LL_DEBUGS() << "Wrapped in conversion to wstring " << wstring_to_utf8str(LLWString( 1, uni_char)) << "\n" << LL_ENDL;
+ LL_DEBUGS() << "Wrapped in conversion to wstring " << ll_convert_to<std::string>(uni_char) << "\n" << LL_ENDL;
- event.insert("char",
- LLSD( wstring_to_utf8str(LLWString( 1,uni_char)) )
- );
+ event.insert("char", LLSD(ll_convert_to<std::string>(uni_char)));
// path (optional) - for now we are not recording path for key events during record - should not be needed for full record and playback of recorded steps
// as a vita script - it does become useful if you edit the resulting vita script and wish to remove some steps leading to a key event - that sort of edit might
diff --git a/indra/newview/llappviewerlistener.cpp b/indra/newview/llappviewerlistener.cpp
index d02b1b4a79..4690c91b61 100644
--- a/indra/newview/llappviewerlistener.cpp
+++ b/indra/newview/llappviewerlistener.cpp
@@ -35,6 +35,7 @@
// external library headers
// other Linden headers
#include "llappviewer.h"
+#include "workqueue.h"
LLAppViewerListener::LLAppViewerListener(const LLAppViewerGetter& getter):
LLEventAPI("LLAppViewer",
@@ -56,17 +57,23 @@ LLAppViewerListener::LLAppViewerListener(const LLAppViewerGetter& getter):
void LLAppViewerListener::userQuit(const LLSD& event)
{
LL_INFOS() << "Listener requested user quit" << LL_ENDL;
- mAppViewerGetter()->userQuit();
+ // Trying to engage this from (e.g.) a Lua-hosting C++ coroutine runs
+ // afoul of an assert in the logging machinery that LLMutex must be locked
+ // only from the main coroutine.
+ LL::WorkQueue::getInstance("mainloop")->post(
+ [appviewer=mAppViewerGetter()]{ appviewer->userQuit(); });
}
void LLAppViewerListener::requestQuit(const LLSD& event)
{
LL_INFOS() << "Listener requested quit" << LL_ENDL;
- mAppViewerGetter()->requestQuit();
+ LL::WorkQueue::getInstance("mainloop")->post(
+ [appviewer=mAppViewerGetter()]{ appviewer->requestQuit(); });
}
void LLAppViewerListener::forceQuit(const LLSD& event)
{
LL_INFOS() << "Listener requested force quit" << LL_ENDL;
- mAppViewerGetter()->forceQuit();
+ LL::WorkQueue::getInstance("mainloop")->post(
+ [appviewer=mAppViewerGetter()]{ appviewer->forceQuit(); });
}
diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp
index aa04221f4b..dded4d1e92 100644
--- a/indra/newview/llfeaturemanager.cpp
+++ b/indra/newview/llfeaturemanager.cpp
@@ -393,7 +393,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];
diff --git a/indra/newview/llglsandbox.cpp b/indra/newview/llglsandbox.cpp
index 0248f5710f..fc02220808 100644
--- a/indra/newview/llglsandbox.cpp
+++ b/indra/newview/llglsandbox.cpp
@@ -923,7 +923,7 @@ struct ShaderProfileHelper
}
~ShaderProfileHelper()
{
- LLGLSLShader::finishProfile(false);
+ LLGLSLShader::finishProfile();
}
};
diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp
index 6a725e785f..0bd21516bc 100644
--- a/indra/newview/llluamanager.cpp
+++ b/indra/newview/llluamanager.cpp
@@ -65,7 +65,7 @@ lua_function(sleep, "sleep(seconds): pause the running coroutine")
// This function consumes ALL Lua stack arguments and returns concatenated
// message string
-std::string lua_print_msg(lua_State* L, const std::string_view& level)
+std::string lua_print_msg(lua_State* L, std::string_view level)
{
// On top of existing Lua arguments, we're going to push tostring() and
// duplicate each existing stack entry so we can stringize each one.
diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp
index cb89a5910e..7f72677e34 100644
--- a/indra/newview/llpanelemojicomplete.cpp
+++ b/indra/newview/llpanelemojicomplete.cpp
@@ -280,8 +280,7 @@ void LLPanelEmojiComplete::onCommit()
{
if (mCurSelected < mTotalEmojis)
{
- LLSD value(wstring_to_utf8str(LLWString(1, mEmojis[mCurSelected].Character)));
- setValue(value);
+ setValue(ll_convert_to<std::string>(mEmojis[mCurSelected].Character));
LLUICtrl::onCommit();
}
}
diff --git a/indra/newview/llurldispatcher.cpp b/indra/newview/llurldispatcher.cpp
index 39a9f0f8bc..166542324d 100644
--- a/indra/newview/llurldispatcher.cpp
+++ b/indra/newview/llurldispatcher.cpp
@@ -289,6 +289,8 @@ public:
LLEventAPI::add("teleport",
"Teleport to specified [\"regionname\"] at\n"
"specified region-relative [\"x\"], [\"y\"], [\"z\"].\n"
+ "If [\"regionname\"] is \"home\", ignore [\"x\"], [\"y\"], [\"z\"]\n"
+ "and teleport home.\n"
"If [\"regionname\"] omitted, teleport to GLOBAL\n"
"coordinates [\"x\"], [\"y\"], [\"z\"].",
&LLTeleportHandler::from_event);
@@ -328,7 +330,12 @@ public:
void from_event(const LLSD& params) const
{
Response response(LLSD(), params);
- if (params.has("regionname"))
+ if (params["regionname"].asString() == "home")
+ {
+ gAgent.teleportHome();
+ response["message"] = "Teleporting home";
+ }
+ else if (params.has("regionname"))
{
// region specified, coordinates (if any) are region-local
LLVector3 local_pos(
diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp
index 301ea5c5f6..fdfe477a6c 100644
--- a/indra/newview/llviewerdisplay.cpp
+++ b/indra/newview/llviewerdisplay.cpp
@@ -28,58 +28,69 @@
#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 "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>
+
+#include <filesystem>
+#include <iomanip>
+#include <sstream>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
@@ -127,6 +138,8 @@ void render_ui_3d();
void render_ui_2d();
void render_disconnected_background();
+std::string getProfileStatsFilename();
+
void display_startup()
{
if ( !gViewerWindow
@@ -1027,10 +1040,49 @@ void display(bool rebuild, F32 zoom_factor, int subfield, bool for_snapshot)
if (gShaderProfileFrame)
{
gShaderProfileFrame = false;
- LLGLSLShader::finishProfile();
+ LLGLSLShader::finishProfile(getProfileStatsFilename());
}
}
+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()
{
diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp
index 9739cac311..1c8f1a32b9 100644
--- a/indra/newview/llviewermedia.cpp
+++ b/indra/newview/llviewermedia.cpp
@@ -2766,7 +2766,8 @@ bool LLViewerMediaImpl::handleUnicodeCharHere(llwchar uni_char)
{
LLSD native_key_data = gViewerWindow->getWindow()->getNativeKeyData();
- mMediaSource->textInput(wstring_to_utf8str(LLWString(1, uni_char)), gKeyboard->currentMask(false), native_key_data);
+ mMediaSource->textInput(ll_convert_to<std::string>(uni_char),
+ gKeyboard->currentMask(false), native_key_data);
}
}
diff --git a/indra/newview/scripts/lua/frame_profile.lua b/indra/newview/scripts/lua/frame_profile.lua
new file mode 100644
index 0000000000..3c6353ff68
--- /dev/null
+++ b/indra/newview/scripts/lua/frame_profile.lua
@@ -0,0 +1,24 @@
+-- Trigger Develop -> Render Tests -> Frame Profile
+
+LLAgent = require 'LLAgent'
+startup = require 'startup'
+Timer = (require 'timers').Timer
+UI = require 'UI'
+
+startup.wait('STATE_STARTED')
+
+-- teleport to http://maps.secondlife.com/secondlife/Bug%20Island/220/224/27
+print(LLAgent.teleport{regionname='Bug Island', x=220, y=224, z=27})
+Timer(10, 'wait')
+LLAgent.setCamera{camera_pos={220, 224, 26}, camera_locked=true,
+ focus_pos ={228, 232, 26}, focus_locked=true}
+Timer(1, 'wait')
+-- This freezes the viewer for perceptible realtime
+UI.popup:tip('starting Render Tests -> Frame Profile')
+UI.call("Advanced.ClickRenderProfile")
+Timer(1, 'wait')
+LLAgent.removeCamParams()
+LLAgent.setFollowCamActive(false)
+
+-- Home, James!
+print(LLAgent.teleport('home'))
diff --git a/indra/newview/scripts/lua/frame_profile_quit.lua b/indra/newview/scripts/lua/frame_profile_quit.lua
new file mode 100644
index 0000000000..e3177a3f67
--- /dev/null
+++ b/indra/newview/scripts/lua/frame_profile_quit.lua
@@ -0,0 +1,25 @@
+-- Trigger Develop -> Render Tests -> Frame Profile and quit
+
+LLAgent = require 'LLAgent'
+logout = require 'logout'
+startup = require 'startup'
+Timer = (require 'timers').Timer
+UI = require 'UI'
+
+startup.wait('STATE_STARTED')
+
+-- Assume we logged into http://maps.secondlife.com/secondlife/Bug%20Island/220/224/27
+-- (see frame_profile bash script)
+Timer(10, 'wait')
+LLAgent.setCamera{camera_pos={220, 224, 26}, camera_locked=true,
+ focus_pos ={228, 232, 26}, focus_locked=true}
+Timer(1, 'wait')
+-- This freezes the viewer for perceptible realtime
+UI.popup:tip('starting Render Tests -> Frame Profile')
+UI.call("Advanced.ClickRenderProfile")
+Timer(1, 'wait')
+LLAgent.removeCamParams()
+LLAgent.setFollowCamActive(false)
+
+-- done
+logout()
diff --git a/indra/newview/scripts/lua/require/LLAgent.lua b/indra/newview/scripts/lua/require/LLAgent.lua
index 07ef1e0b0b..5cee998fcd 100644
--- a/indra/newview/scripts/lua/require/LLAgent.lua
+++ b/indra/newview/scripts/lua/require/LLAgent.lua
@@ -71,4 +71,13 @@ function LLAgent.getAnimationInfo(item_id)
return leap.request('LLAgent', {op = 'getAnimationInfo', item_id=item_id}).anim_info
end
+-- Teleport to specified "regionname" at specified region-relative "x", "y", "z".
+-- If "regionname" is "home", ignore "x", "y", "z" and teleport home.
+-- If "regionname" omitted, teleport to GLOBAL coordinates "x", "y", "z".
+function LLAgent.teleport(...)
+ local args = mapargs('regionname,x,y,z', ...)
+ args.op = 'teleport'
+ return leap.request('LLTeleportHandler', args).message
+end
+
return LLAgent
diff --git a/indra/newview/scripts/lua/test_animation.lua b/indra/newview/scripts/lua/test_animation.lua
index c16fef4918..37e7254a6c 100644
--- a/indra/newview/scripts/lua/test_animation.lua
+++ b/indra/newview/scripts/lua/test_animation.lua
@@ -11,18 +11,22 @@ for key in pairs(anims) do
table.insert(anim_ids, key)
end
--- Start playing a random animation
-math.randomseed(os.time())
-local random_id = anim_ids[math.random(#anim_ids)]
-local anim_info = LLAgent.getAnimationInfo(random_id)
+if #anim_ids == 0 then
+ print("No animations found")
+else
+ -- Start playing a random animation
+ math.randomseed(os.time())
+ local random_id = anim_ids[math.random(#anim_ids)]
+ local anim_info = LLAgent.getAnimationInfo(random_id)
-print("Starting animation locally: " .. anims[random_id].name)
-print("Loop: " .. anim_info.is_loop .. " Joints: " .. anim_info.num_joints .. " Duration " .. tonumber(string.format("%.2f", anim_info.duration)))
-LLAgent.playAnimation{item_id=random_id}
+ print("Starting animation locally: " .. anims[random_id].name)
+ print("Loop: " .. anim_info.is_loop .. " Joints: " .. anim_info.num_joints .. " Duration " .. tonumber(string.format("%.2f", anim_info.duration)))
+ LLAgent.playAnimation{item_id=random_id}
--- Stop animation after 3 sec if it's looped or longer than 3 sec
-if anim_info.is_loop == 1 or anim_info.duration > 3 then
- LL.sleep(3)
- print("Stop animation.")
- LLAgent.stopAnimation(random_id)
+ -- Stop animation after 3 sec if it's looped or longer than 3 sec
+ if anim_info.is_loop == 1 or anim_info.duration > 3 then
+ LL.sleep(3)
+ print("Stop animation.")
+ LLAgent.stopAnimation(random_id)
+ end
end
diff --git a/indra/newview/tests/llluamanager_test.cpp b/indra/newview/tests/llluamanager_test.cpp
index 8d1333815b..f0a1b32eed 100644
--- a/indra/newview/tests/llluamanager_test.cpp
+++ b/indra/newview/tests/llluamanager_test.cpp
@@ -123,7 +123,7 @@ namespace tut
}
}
- void from_lua(const std::string& desc, const std::string_view& construct, const LLSD& expect)
+ void from_lua(const std::string& desc, std::string_view construct, const LLSD& expect)
{
LLSD fromlua;
LLStreamListener pump("testpump",
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py
index aa2b0b0e25..9f77dba3bc 100755
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -166,11 +166,7 @@ class ViewerManifest(LLManifest):
self.path("*/*/*/*.js")
self.path("*/*/*.html")
- with self.prefix(src_dst="scripts/lua"):
- self.path("*.lua")
- self.path("*.xml")
- with self.prefix(src_dst='require'):
- self.path("*.lua")
+ self.path('scripts/lua')
#build_data.json. Standard with exception handling is fine. If we can't open a new file for writing, we have worse problems
#platform is computed above with other arg parsing
diff --git a/indra/test/namedtempfile.h b/indra/test/namedtempfile.h
index 8027f95728..96b19523ab 100644
--- a/indra/test/namedtempfile.h
+++ b/indra/test/namedtempfile.h
@@ -32,18 +32,18 @@ class NamedTempFile: public boost::noncopyable
{
LOG_CLASS(NamedTempFile);
public:
- NamedTempFile(const std::string_view& pfx,
- const std::string_view& content,
- const std::string_view& sfx=std::string_view(""))
+ NamedTempFile(std::string_view pfx,
+ std::string_view content,
+ std::string_view sfx=std::string_view(""))
{
createFile(pfx, [&content](std::ostream& out){ out << content; }, sfx);
}
// Disambiguate when passing string literal -- unclear why a string
// literal should be ambiguous wrt std::string_view and Streamer
- NamedTempFile(const std::string_view& pfx,
+ NamedTempFile(std::string_view pfx,
const char* content,
- const std::string_view& sfx=std::string_view(""))
+ std::string_view sfx=std::string_view(""))
{
createFile(pfx, [&content](std::ostream& out){ out << content; }, sfx);
}
@@ -53,9 +53,9 @@ public:
// (boost::phoenix::placeholders::arg1 << "the value is " << 17 << '\n')
typedef std::function<void(std::ostream&)> Streamer;
- NamedTempFile(const std::string_view& pfx,
+ NamedTempFile(std::string_view pfx,
const Streamer& func,
- const std::string_view& sfx=std::string_view(""))
+ std::string_view sfx=std::string_view(""))
{
createFile(pfx, func, sfx);
}
@@ -94,8 +94,8 @@ public:
return out;
}
- static boost::filesystem::path temp_path(const std::string_view& pfx="",
- const std::string_view& sfx="")
+ static boost::filesystem::path temp_path(std::string_view pfx="",
+ std::string_view sfx="")
{
// This variable is set by GitHub actions and is the recommended place
// to put temp files belonging to an actions job.
@@ -114,9 +114,9 @@ public:
}
protected:
- void createFile(const std::string_view& pfx,
+ void createFile(std::string_view pfx,
const Streamer& func,
- const std::string_view& sfx)
+ std::string_view sfx)
{
// Create file in a temporary place.
mPath = temp_path(pfx, sfx);
@@ -137,7 +137,7 @@ class NamedExtTempFile: public NamedTempFile
{
LOG_CLASS(NamedExtTempFile);
public:
- NamedExtTempFile(const std::string& ext, const std::string_view& content):
+ NamedExtTempFile(const std::string& ext, std::string_view content):
NamedTempFile(remove_dot(ext), content, ensure_dot(ext))
{}
diff --git a/scripts/perf/frame_profile b/scripts/perf/frame_profile
new file mode 100755
index 0000000000..0a4e0a74ff
--- /dev/null
+++ b/scripts/perf/frame_profile
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+
+exe="$1"
+if [[ "$OSTYPE" == darwin* && -d "$exe" && "$exe" == *.app ]]
+then
+ exe="$(ls "$exe/Contents/MacOS/Second Life "*)"
+fi
+
+"$exe" --autologin --luafile frame_profile_quit.lua \
+ http://maps.secondlife.com/secondlife/Bug%20Island/220/224/27
diff --git a/scripts/perf/profile_csv.py b/scripts/perf/profile_csv.py
new file mode 100644
index 0000000000..273e3b7434
--- /dev/null
+++ b/scripts/perf/profile_csv.py
@@ -0,0 +1,72 @@
+#!/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 logsdir
+import json
+from pathlib import Path
+import sys
+
+class Error(Exception):
+ pass
+
+def convert(path, totals=True, unused=True, file=sys.stdout):
+ with open(path) as inf:
+ data = json.load(inf)
+ 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)
+ if not args.path:
+ logs = logsdir.logsdir()
+ profiles = Path(logs).glob('profile.*.json')
+ sort = [(p.stat().st_mtime, p) for p in profiles]
+ sort.sort(reverse=True)
+ try:
+ args.path = sort[0][1]
+ except IndexError:
+ raise Error(f'No profile.*.json files in {logs}')
+ # print path to sys.stderr in case user is redirecting stdout
+ print(args.path, file=sys.stderr)
+
+ convert(args.path, 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))
diff --git a/scripts/perf/profile_pretty.py b/scripts/perf/profile_pretty.py
new file mode 100644
index 0000000000..15b6efd94d
--- /dev/null
+++ b/scripts/perf/profile_pretty.py
@@ -0,0 +1,54 @@
+#!/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 logsdir
+import json
+from pathlib import Path
+import sys
+
+class Error(Exception):
+ pass
+
+def pretty(path):
+ with open(path) as inf:
+ data = json.load(inf)
+ 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)
+ if not args.path:
+ logs = logsdir.logsdir()
+ profiles = Path(logs).glob('profile.*.json')
+ sort = [(p.stat().st_mtime, p) for p in profiles]
+ sort.sort(reverse=True)
+ try:
+ args.path = sort[0][1]
+ except IndexError:
+ raise Error(f'No profile.*.json files in {logs}')
+ # print path to sys.stderr in case user is redirecting stdout
+ print(args.path, file=sys.stderr)
+
+ pretty(args.path)
+
+if __name__ == "__main__":
+ try:
+ sys.exit(main(*sys.argv[1:]))
+ except (Error, OSError, json.JSONDecodeError) as err:
+ sys.exit(str(err))