From f037cde7f4d5d55dc0a71eb867f5b2bfcaf5631f Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 11 Sep 2024 12:29:18 -0400 Subject: Make Develop->Render Tests->Frame Profile dump JSON to a file too. Make `LLGLSLShader::finishProfile()` accept a string pathname instead of a bool and, in addition to logging statistics to the viewer log, output statistics to that file as JSON. The calls that used to pass `emit_report=false` now pass `report_name=std::string()`. Make llviewerdisplay.cpp's `display()` function synthesize a profile filename in the viewer's logs directory, and pass that filename to `LLGLSLShader::finishProfile()`. (cherry picked from commit d5712689d36a1ee1af32242706901fde7229b08d) --- indra/llrender/llglslshader.cpp | 120 +++++++++++++++++++++++++--------------- indra/llrender/llglslshader.h | 5 +- 2 files changed, 79 insertions(+), 46 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index a157bfee21..bb734971d5 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -41,6 +41,8 @@ #include "OpenGL/OpenGL.h" #endif +#include + // 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 @@ -101,9 +103,9 @@ void LLGLSLShader::initProfile() sTotalSamplesDrawn = 0; sTotalBinds = 0; - for (std::set::iterator iter = sInstances.begin(); iter != sInstances.end(); ++iter) + for (auto ptr : sInstances) { - (*iter)->clearStats(); + ptr->clearStats(); } } @@ -117,48 +119,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 sorted; - - for (std::set::iterator iter = sInstances.begin(); iter != sInstances.end(); ++iter) - { - sorted.push_back(*iter); - } - + std::vector 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::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::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 +195,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..efcbaf42e8 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 #include 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={}); 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 -- cgit v1.2.3 From a3d6544be07e3cd0bb7e4cb78564a0d6077fd910 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 12 Sep 2024 09:12:33 -0400 Subject: Give `LLGLSLShader::finishProfile()` a static default string param. `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. (cherry picked from commit c6e6f44f50b4de391000c5b9f781a2f0a5024e76) --- indra/llrender/llglslshader.cpp | 1 + indra/llrender/llglslshader.h | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index bb734971d5..56f1533708 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -65,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; diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h index efcbaf42e8..a9b9bfafa8 100644 --- a/indra/llrender/llglslshader.h +++ b/indra/llrender/llglslshader.h @@ -170,7 +170,7 @@ public: static U32 sMaxGLTFNodes; static void initProfile(); - static void finishProfile(const std::string& report_name={}); + static void finishProfile(const std::string& report_name=sDefaultReportName); static void startProfile(); static void stopProfile(); @@ -364,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) -- cgit v1.2.3 From 8c40e6f0a9e211ec22331385dc66b5ff5233859c Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 19 Sep 2024 11:46:08 -0400 Subject: Reapply commit f4b6563 -- cherry-picking lost parts of it?! --- indra/llrender/llglslshader.cpp | 21 ++++----------------- indra/llrender/llglslshader.h | 10 +++++----- 2 files changed, 9 insertions(+), 22 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index 56f1533708..6ba5463acd 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -41,8 +41,6 @@ #include "OpenGL/OpenGL.h" #endif -#include - // 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 @@ -65,7 +63,7 @@ U64 LLGLSLShader::sTotalTimeElapsed = 0; U32 LLGLSLShader::sTotalTrianglesDrawn = 0; U64 LLGLSLShader::sTotalSamplesDrawn = 0; U32 LLGLSLShader::sTotalBinds = 0; -std::string LLGLSLShader::sDefaultReportName; +boost::json::value LLGLSLShader::sDefaultStats; //UI shader -- declared here so llui_libtest will link properly LLGLSLShader gUIProgram; @@ -120,16 +118,16 @@ struct LLGLSLShaderCompareTimeElapsed }; //static -void LLGLSLShader::finishProfile(const std::string& report_name) +void LLGLSLShader::finishProfile(boost::json::value& statsv) { sProfileEnabled = false; - if (! report_name.empty()) + if (! statsv.is_null()) { std::vector sorted(sInstances.begin(), sInstances.end()); std::sort(sorted.begin(), sorted.end(), LLGLSLShaderCompareTimeElapsed()); - boost::json::object stats; + auto& stats = statsv.as_object(); auto shadersit = stats.emplace("shaders", boost::json::array_kind).first; auto& shaders = shadersit->value().as_array(); bool unbound = false; @@ -174,17 +172,6 @@ void LLGLSLShader::finishProfile(const std::string& report_name) } } } - - 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; - } } } diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h index a9b9bfafa8..2d669c70a9 100644 --- a/indra/llrender/llglslshader.h +++ b/indra/llrender/llglslshader.h @@ -170,7 +170,7 @@ public: static U32 sMaxGLTFNodes; static void initProfile(); - static void finishProfile(const std::string& report_name=sDefaultReportName); + static void finishProfile(boost::json::value& stats=sDefaultStats); static void startProfile(); static void stopProfile(); @@ -365,10 +365,10 @@ 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; + // 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) -- cgit v1.2.3