diff --git a/CMakeLists.txt b/CMakeLists.txt index b4477e2..b734d6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,10 +76,15 @@ target_compile_definitions(${TARGET_NAME} PUBLIC -DBUILD_CTR=1 ) -add_executable(test test/main.cpp) -target_include_directories(test PUBLIC include test) +add_executable(test test/app/main.cpp) +target_include_directories(test PUBLIC include test/app) target_link_directories(test PUBLIC ${CMAKE_BINARY_DIR}) target_link_libraries(test PUBLIC palladium citro3d ctru m) + +add_executable(testbench test/bench/main.cpp) +target_include_directories(testbench PUBLIC include test/bench) +target_link_directories(testbench PUBLIC ${CMAKE_BINARY_DIR}) +target_link_libraries(testbench PUBLIC palladium citro3d ctru m) # Generate 3DSX ctr_generate_smdh( ${CMAKE_BINARY_DIR}/test.smdh @@ -94,5 +99,11 @@ ctr_create_3dsx( SMDH "${CMAKE_BINARY_DIR}/test.smdh" ROMFS "${CMAKE_SOURCE_DIR}/test/romfs" ) +ctr_create_3dsx( + testbench + OUTPUT "${CMAKE_BINARY_DIR}/testbench.3dsx" + SMDH "${CMAKE_BINARY_DIR}/test.smdh" + ROMFS "${CMAKE_SOURCE_DIR}/test/romfs" +) install(TARGETS ${TARGET_NAME}) install(DIRECTORY include DESTINATION ".") \ No newline at end of file diff --git a/include/pd/common/timetrace.hpp b/include/pd/common/timetrace.hpp index a38993a..56696b9 100644 --- a/include/pd/common/timetrace.hpp +++ b/include/pd/common/timetrace.hpp @@ -31,23 +31,47 @@ class TimeStats : public SmartCtor { TimeStats(int l) : len(l), val(l, 0) {} ~TimeStats() = default; - void Add(unsigned long long v) { + void Add(u64 v) { val[idx] = v; idx = next(idx); num_val = std::min(num_val + 1, len); } - unsigned long long GetAverage() { + u64 GetAverage() { if (!num_val) return 0.f; - unsigned long long res = 0; + u64 res = 0; for (int i = 0; i < num_val; i++) { res += val[smart_idx(i)]; } return res / num_val; } - const std::vector &GetData() { return val; } - const unsigned long long &operator[](int i) { return val[smart_idx(i)]; } + u64 GetMin() { + if (!num_val) return 0.f; + u64 res = std::numeric_limits::max(); + for (int i = 0; i < num_val; i++) { + res = std::min(val[smart_idx(i)], res); + } + return res; + } + + u64 GetMax() { + if (!num_val) return 0.f; + u64 res = 0; + for (int i = 0; i < num_val; i++) { + res = std::max(val[smart_idx(i)], res); + } + return res; + } + + void Clear() { + val.assign(len, 0); + idx = 0; + num_val = 0; + } + + const std::vector &GetData() { return val; } + const u64 &operator[](int i) { return val[smart_idx(i)]; } const size_t GetLen() { return len; } const size_t GetNumValues() { return num_val; } @@ -56,7 +80,7 @@ class TimeStats : public SmartCtor { size_t smart_idx(size_t v) const { return (idx + len - num_val + v) % len; } int len = 0; - std::vector val; + std::vector val; int idx = 0; int num_val = 0; }; @@ -68,21 +92,21 @@ class Res : public SmartCtor { void SetID(const std::string &v) { id = v; } const std::string GetID() { return id; } - void SetStart(unsigned long long v) { start = v; } - unsigned long long GetStart() { return start; } - void SetEnd(unsigned long long v) { + void SetStart(u64 v) { start = v; } + u64 GetStart() { return start; } + void SetEnd(u64 v) { end = v; protocol->Add(GetLastDiff()); } - unsigned long long GetEnd() { return end; } + u64 GetEnd() { return end; } - unsigned long long GetLastDiff() { return end - start; } + u64 GetLastDiff() { return end - start; } TimeStats::Ref GetProtocol() { return protocol; } private: std::string id; - unsigned long long start; - unsigned long long end; + u64 start; + u64 end; TimeStats::Ref protocol; }; void Beg(const std::string &id); diff --git a/test/main.cpp b/test/app/main.cpp similarity index 100% rename from test/main.cpp rename to test/app/main.cpp diff --git a/test/bench/main.cpp b/test/bench/main.cpp new file mode 100644 index 0000000..73fd1bc --- /dev/null +++ b/test/bench/main.cpp @@ -0,0 +1,178 @@ +#include + +struct StatsRes { + std::string name; + std::string average; + std::string min; + std::string max; +}; + +class TestBench : public PD::App { + public: + TestBench() = default; + ~TestBench() = default; + + void Init() override { + /// Test in OLD3DS Mode + osSetSpeedupEnable(false); + PD::TT::Beg("BaseInit"); + inp = Input(); + ren = Renderer(); + font = PD::LI::Font::New(); + font->LoadTTF("romfs:/fonts/JetBrainsMono-Medium.ttf", 32); + ren->Font(font); + ui7 = PD::UI7::Context::New(ren, inp); + /// Maximum frames for 5 seconds + li_stats = PD::TimeStats::New(300); + PD::TT::End("BaseInit"); + } + + bool MainLoop(float delta, float time) { + if (stime == 0.f) { + stime = time; + } + switch (test) { + case 0: + Test1(time); + break; + case 1: + Test2(delta, time); + break; + default: + Result(delta); + } + if (time - stime > 5.f && test < 2) { + if (test == 0) { + results.push_back(std::make_pair>( + std::to_string(test + 1), + {MakeRes("LI", li_stats), + MakeRes("Scene1", PD::Sys::GetTraceRef("Test1")->GetProtocol())})); + } else if (test == 1) { + results.push_back(std::make_pair>( + std::to_string(test + 1), + {MakeRes("LI", li_stats), + MakeRes("Scene2", PD::Sys::GetTraceRef("Test2")->GetProtocol())})); + } else { + results.push_back(std::make_pair>( + std::to_string(test + 1), {MakeRes("LI", li_stats)})); + } + test++; + stime = time; + frame = 0; + li_stats->Clear(); + } + frame++; + return true; + } + + StatsRes MakeRes(const std::string& name, PD::TimeStats::Ref s) { + StatsRes res; + res.name = name; + res.average = PD::Strings::FormatNanos(s->GetAverage()); + res.min = PD::Strings::FormatNanos(s->GetMin()); + res.max = PD::Strings::FormatNanos(s->GetMax()); + return res; + } + + void Result(float delta) { + UpdateLiTimes(); + ren->OnScreen(PD::Screen::Top); + if (ui7->BeginMenu("TestBench")) { + auto m = ui7->GetCurrentMenu(); + m->Label("Base Init: " + TTime("BaseInit")); + m->Label("Render All: " + + PD::Strings::FormatNanos(li_stats->GetAverage())); + ui7->EndMenu(); + } + ren->OnScreen(PD::Screen::Bottom); + if (ui7->BeginMenu("Test Results", UI7MenuFlags_Scrolling)) { + auto m = ui7->GetCurrentMenu(); + for (auto& it : results) { + m->SeparatorText("Test " + it.first); + for (int i = 0; i < (int)it.second.size(); i++) { + m->Label(it.second[i].name + ":"); + m->Label("AVG: " + it.second[i].average); + m->Label("MIN: " + it.second[i].min); + m->Label("MAX: " + it.second[i].max); + if (i != (int)it.second.size() - 1) { + m->Separator(); + } + } + } + ui7->EndMenu(); + } + ui7->Update(delta); + } + + void Test1(float time) { + PD::TT::Scope st("Test1"); + UpdateLiTimes(); + DrawFancyBG(time); + } + + void Test2(float delta, float time) { + PD::TT::Scope st("Test2"); + UpdateLiTimes(); + DrawFancyBG(time); + if (ui7->BeginMenu("Test2")) { + auto m = ui7->GetCurrentMenu(); + m->Button("Test"); + m->Separator(); + m->Label("Line1"); + m->Label("Line2"); + ui7->EndMenu(); + } + ui7->Update(delta); + } + + void DrawFancyBG(float time) { + ren->DrawRect(vec2(0, 0), vec2(400, 240), 0xff64c9fd); + for (int i = 0; i < 44; i++) Append(i, vec2(0, 0), vec2(400, 240), time); + } + + float Offset(float x) { + float y = cos(x) * 42; + return y - floor(y); + } + void Append(int index, vec2 position, vec2 size, float time) { + float offset = Offset(index) * 62; + float x_position = position.x() + size.x() / 8 * ((index % 11) - 1) + + cos(offset + time) * 10; + float y_position = position.y() + size.y() / 8 * (index / 11) + 40 + + sin(offset + time) * 10 + 30; + float color_effect = 1 - exp(-(index / 11) / 3.0f); + + ren->DrawTriangle( + vec2(x_position, y_position), vec2(x_position + 300, y_position + (90)), + vec2(x_position - 300, y_position + (90)), + PD::Color(.94f - .17f * color_effect, .61f - .25f * color_effect, + .36f + .38f * color_effect)); + } + + std::string TTime(const std::string& id) { + return PD::Strings::FormatNanos(PD::Sys::GetTraceRef(id)->GetLastDiff()); + } + + void UpdateLiTimes() { + if (frame) { + li_stats->Add(PD::Sys::GetTraceRef("LI_RenderAll")->GetLastDiff()); + } + } + + private: + PD::Hid::Ref inp; + PD::LI::Renderer::Ref ren; + PD::UI7::Context::Ref ui7; + PD::LI::Font::Ref font; + PD::TimeStats::Ref li_stats; + int test = 0; + float stime = 0.f; + std::vector>> results; + int frame = 0; +}; + +int main() { + TestBench app; + app.Run(); + return 0; +} \ No newline at end of file