Merge branch 'develop' of https://github.com/secondlife/viewer
# Conflicts: # indra/newview/llappviewer.cpp # indra/newview/llviewerdisplay.cpp # indra/newview/llviewermedia.cppmaster
commit
193fb1627f
|
|
@ -5,6 +5,8 @@
|
|||
a0b3021bdcf76859054fda8e30abb3ed47749e83
|
||||
8444cd9562a6a7b755fcb075864e205122354192
|
||||
863c541ce0b2e3e1e566cc88423d3e87aaedb6ca
|
||||
743a1a6d8eabf069d95777c96e5b657cb8702593
|
||||
4a00da1ada89af6f313cee30f5177634b0b180a9
|
||||
# Wrong line endings
|
||||
1b67dd855c41f5a0cda7ec2a68d98071986ca703
|
||||
6cc7dd09d5e69cf57e6de7fb568a0ad2693f9c9a
|
||||
|
|
|
|||
9
build.sh
9
build.sh
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) */
|
||||
|
|
@ -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) */
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ private:
|
|||
|
||||
public:
|
||||
template<bool SHARED>
|
||||
class DataLock : LLSharedMutexLockTemplate<SHARED>
|
||||
class DataLock : public LLSharedMutexLockTemplate<SHARED>
|
||||
{
|
||||
public:
|
||||
DataLock(const LLImageBase* image)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
7.1.11
|
||||
7.1.12
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -941,7 +941,7 @@ struct ShaderProfileHelper
|
|||
}
|
||||
~ShaderProfileHelper()
|
||||
{
|
||||
LLGLSLShader::finishProfile(false);
|
||||
LLGLSLShader::finishProfile();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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}')
|
||||
|
|
@ -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))
|
||||
|
|
@ -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))
|
||||
|
|
@ -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))
|
||||
Loading…
Reference in New Issue