diff --git a/src/utils/debug.cpp b/src/utils/debug.cpp index 871c4ae9a..1f01875bd 100644 --- a/src/utils/debug.cpp +++ b/src/utils/debug.cpp @@ -29,6 +29,7 @@ #include "main_loop.hpp" #include "replay/replay_recorder.hpp" #include "utils/log.hpp" +#include "utils/profiler.hpp" #include #include using namespace irr; @@ -57,6 +58,7 @@ enum DebugMenuCommand DEBUG_GRAPHICS_BULLET_1, DEBUG_GRAPHICS_BULLET_2, DEBUG_PROFILER, + DEBUG_PROFILER_GENERATE_REPORT, DEBUG_FPS, DEBUG_SAVE_REPLAY, DEBUG_SAVE_HISTORY, @@ -135,6 +137,8 @@ bool onEvent(const SEvent &event) sub->addItem(L"Nitro", DEBUG_POWERUP_NITRO ); mnu->addItem(L"Profiler",DEBUG_PROFILER); + if (UserConfigParams::m_profiler_enabled) + mnu->addItem(L"Toggle capture profiler report", DEBUG_PROFILER_GENERATE_REPORT); mnu->addItem(L"Do not limit FPS", DEBUG_THROTTLE_FPS); mnu->addItem(L"FPS",DEBUG_FPS); mnu->addItem(L"Save replay", DEBUG_SAVE_REPLAY); @@ -254,6 +258,10 @@ bool onEvent(const SEvent &event) UserConfigParams::m_profiler_enabled = !UserConfigParams::m_profiler_enabled; } + else if (cmdID == DEBUG_PROFILER_GENERATE_REPORT) + { + profiler.setCaptureReport(!profiler.getCaptureReport()); + } else if (cmdID == DEBUG_THROTTLE_FPS) { main_loop->setThrottleFPS(false); diff --git a/src/utils/profiler.cpp b/src/utils/profiler.cpp index b9fc0b4fe..2f8b5bcc6 100644 --- a/src/utils/profiler.cpp +++ b/src/utils/profiler.cpp @@ -21,6 +21,7 @@ #include "guiengine/event_handler.hpp" #include "guiengine/engine.hpp" #include "guiengine/scalable_font.hpp" +#include "io/xml_writer.hpp" #include #include #include @@ -71,6 +72,9 @@ Profiler::Profiler() m_time_last_sync = _getTimeMilliseconds(); m_time_between_sync = 0.0; m_freeze_state = UNFROZEN; + m_capture_report = false; + m_first_capture_sweep = true; + m_capture_report_buffer = NULL; } //----------------------------------------------------------------------------- @@ -78,6 +82,31 @@ Profiler::~Profiler() { } +//----------------------------------------------------------------------------- + +void Profiler::setCaptureReport(bool captureReport) +{ + if (!m_capture_report && captureReport) + { + m_capture_report = true; + m_first_capture_sweep = true; + // TODO: a 20 MB hardcoded buffer for now. That should amply suffice for + // all reasonable purposes. But it's not too clean to hardcode + m_capture_report_buffer = new StringBuffer(20 * 1024 * 1024); + } + else if (m_capture_report && !captureReport) + { + // when disabling capture to file, flush captured data to a file + { + XMLWriter writer(file_manager->getUserConfigFile("profiling.csv").c_str()); + writer << m_capture_report_buffer->getRawBuffer(); + } + m_capture_report = false; + delete m_capture_report_buffer; + m_capture_report_buffer = NULL; + } +} + //----------------------------------------------------------------------------- /// Push a new marker that starts now void Profiler::pushCpuMarker(const char* name, const video::SColor& color) @@ -194,7 +223,7 @@ void Profiler::draw() // Force to show the pointer irr_driver->showPointer(); - int read_id = !m_write_id; + int read_id = (m_freeze_state == FROZEN ? !m_write_id : m_write_id); // Compute some values for drawing (unit: pixels, but we keep floats for reducing errors accumulation) core::dimension2d screen_size = driver->getScreenSize(); @@ -232,19 +261,34 @@ void Profiler::draw() core::vector2di mouse_pos = GUIEngine::EventHandler::get()->getMousePos(); // For each thread: - for(size_t i=0 ; i < nb_thread_infos ; i++) + for (size_t i = 0; i < nb_thread_infos; i++) { // Draw all markers MarkerList& markers = m_thread_infos[i].markers_done[read_id]; - if(markers.empty()) + if (markers.empty()) continue; + if (m_capture_report) + { + if (m_first_capture_sweep) + m_capture_report_buffer->getStdStream() << "\"Thread\";"; + else + m_capture_report_buffer->getStdStream() << i << ";"; + } MarkerList::const_iterator it_end = markers.end(); - for(MarkerList::const_iterator it = markers.begin() ; it != it_end ; it++) + for (MarkerList::const_iterator it = markers.begin(); it != it_end; it++) { const Marker& m = *it; assert(m.end >= 0.0); + + if (m_capture_report) + { + if (m_first_capture_sweep) + m_capture_report_buffer->getStdStream() << "\"" << m.name << "\";"; + else + m_capture_report_buffer->getStdStream() << (int)std::round((m.end - m.start) * 1000) << ";"; + } core::rect pos((s32)( x_offset + factor*m.start ), (s32)( y_offset + i*line_height ), (s32)( x_offset + factor*m.end ), @@ -260,6 +304,12 @@ void Profiler::draw() if(pos.isPointInside(mouse_pos)) hovered_markers.push(m); } + + if (m_capture_report) + { + m_capture_report_buffer->getStdStream() << "\n"; + m_first_capture_sweep = false; + } } // Draw the end of the frame diff --git a/src/utils/profiler.hpp b/src/utils/profiler.hpp index 738e2e633..99759413e 100644 --- a/src/utils/profiler.hpp +++ b/src/utils/profiler.hpp @@ -23,6 +23,8 @@ #include #include #include +#include + class Profiler; extern Profiler profiler; @@ -50,6 +52,41 @@ extern Profiler profiler; using namespace irr; +/** For profiling reports, we need a custom strijng stream that writes to a large + pre-allocated buffer, to avoid allocating as much as possible durign profiling */ +template +struct ostreambuf : public std::basic_streambuf > +{ + ostreambuf(char_type* buffer, std::streamsize bufferLength) + { + // set the "put" pointer the start of the buffer and record it's length. + setp(buffer, buffer + bufferLength); + } +}; + +class StringBuffer +{ +private: + char* m_buffer; + ostreambuf ostreamBuffer; + std::ostream messageStream; + +public: + + StringBuffer(unsigned int size) : m_buffer(new char[size]), ostreamBuffer(m_buffer, size), messageStream(&ostreamBuffer) + { + } + + ~StringBuffer() + { + delete[] m_buffer; + } + + std::ostream& getStdStream() { return messageStream; } + + char* getRawBuffer() { return m_buffer; } +}; + /** * \brief class that allows run-time graphical profiling through the use of markers * \ingroup utils @@ -104,6 +141,10 @@ private: FreezeState m_freeze_state; + bool m_capture_report; + bool m_first_capture_sweep; + StringBuffer* m_capture_report_buffer; + public: Profiler(); virtual ~Profiler(); @@ -116,10 +157,13 @@ public: void onClick(const core::vector2di& mouse_pos); + bool getCaptureReport() const { return m_capture_report; } + void setCaptureReport(bool captureReport); protected: // TODO: detect on which thread this is called to support multithreading ThreadInfo& getThreadInfo() { return m_thread_infos[0]; } void drawBackground(); + }; #endif // PROFILER_HPP