diff --git a/direct/src/task/TaskNew.py b/direct/src/task/TaskNew.py index 0a95aed1e7..018a20330f 100644 --- a/direct/src/task/TaskNew.py +++ b/direct/src/task/TaskNew.py @@ -235,3 +235,6 @@ class TaskManager: def stop(self): # Set a flag so we will stop before beginning next frame self.running = False + + def __repr__(self): + return str(self.mgr) diff --git a/panda/src/event/asyncTask.I b/panda/src/event/asyncTask.I index 7707d2620f..890105f423 100644 --- a/panda/src/event/asyncTask.I +++ b/panda/src/event/asyncTask.I @@ -13,29 +13,6 @@ //////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////// -// Function: AsyncTask::Constructor -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -INLINE AsyncTask:: -AsyncTask(const string &name) : - Namable(name), - _delay(0.0), - _has_delay(false), - _wake_time(0.0), - _sort(0), - _priority(0), - _state(S_inactive), - _servicing_thread(NULL), - _manager(NULL), - _chain(NULL) -{ -#ifdef HAVE_PYTHON - _python_object = NULL; -#endif // HAVE_PYTHON -} - //////////////////////////////////////////////////////////////////// // Function: AsyncTask::get_state // Access: Published @@ -256,3 +233,39 @@ get_python_object() const { } #endif // HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: AsyncTask::get_dt +// Access: Published +// Description: Returns the amount of time elapsed during the task's +// previous run cycle, in seconds. +//////////////////////////////////////////////////////////////////// +INLINE double AsyncTask:: +get_dt() const { + return _dt; +} + +//////////////////////////////////////////////////////////////////// +// Function: AsyncTask::get_max_dt +// Access: Published +// Description: Returns the maximum amount of time elapsed during any +// one of the task's previous run cycles, in seconds. +//////////////////////////////////////////////////////////////////// +INLINE double AsyncTask:: +get_max_dt() const { + return _max_dt; +} + +//////////////////////////////////////////////////////////////////// +// Function: AsyncTask::get_average_dt +// Access: Published +// Description: Returns the average amount of time elapsed during +// each of the task's previous run cycles, in seconds. +//////////////////////////////////////////////////////////////////// +INLINE double AsyncTask:: +get_average_dt() const { + if (_num_frames == 0) { + return 0.0; + } else { + return _total_dt / _num_frames; + } +} diff --git a/panda/src/event/asyncTask.cxx b/panda/src/event/asyncTask.cxx index 822c5b707e..625869b718 100644 --- a/panda/src/event/asyncTask.cxx +++ b/panda/src/event/asyncTask.cxx @@ -18,6 +18,33 @@ TypeHandle AsyncTask::_type_handle; +//////////////////////////////////////////////////////////////////// +// Function: AsyncTask::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +AsyncTask:: +AsyncTask(const string &name) : + Namable(name), + _delay(0.0), + _has_delay(false), + _wake_time(0.0), + _sort(0), + _priority(0), + _state(S_inactive), + _servicing_thread(NULL), + _manager(NULL), + _chain(NULL), + _dt(0.0), + _max_dt(0.0), + _total_dt(0.0), + _num_frames(0) +{ +#ifdef HAVE_PYTHON + _python_object = NULL; +#endif // HAVE_PYTHON +} + //////////////////////////////////////////////////////////////////// // Function: AsyncTask::Destructor // Access: Public, Virtual @@ -47,7 +74,7 @@ remove() { // Function: AsyncTask::get_elapsed_time // Access: Published // Description: Returns the amount of time that has elapsed since -// task was started, according to the task manager's +// the task was started, according to the task manager's // clock. // // It is only valid to call this if the task's status is @@ -224,6 +251,36 @@ output(ostream &out) const { } } +//////////////////////////////////////////////////////////////////// +// Function: AsyncTask::unlock_and_do_task +// Access: Protected +// Description: Called by the AsyncTaskManager to actually run the +// task. Assumes the lock is held. See do_task(). +//////////////////////////////////////////////////////////////////// +AsyncTask::DoneStatus AsyncTask:: +unlock_and_do_task() { + nassertr(_manager != (AsyncTaskManager *)NULL, DS_done); + PT(ClockObject) clock = _manager->get_clock(); + + // It's important to release the lock while the task is being + // serviced. + _manager->_lock.release(); + + double start = clock->get_real_time(); + DoneStatus status = do_task(); + double end = clock->get_real_time(); + + // Now reacquire the lock (so we can return with the lock held). + _manager->_lock.lock(); + + _dt = end - start; + _max_dt = max(_dt, _max_dt); + _total_dt += _dt; + ++_num_frames; + + return status; +} + //////////////////////////////////////////////////////////////////// // Function: AsyncTask::do_task // Access: Protected, Virtual @@ -241,6 +298,8 @@ output(ostream &out) const { // // DS_abort: abort the task, and interrupt the whole // AsyncTaskManager. +// +// This function is called with the lock *not* held. //////////////////////////////////////////////////////////////////// AsyncTask::DoneStatus AsyncTask:: do_task() { diff --git a/panda/src/event/asyncTask.h b/panda/src/event/asyncTask.h index b300a80494..edda51a1e4 100644 --- a/panda/src/event/asyncTask.h +++ b/panda/src/event/asyncTask.h @@ -42,7 +42,7 @@ class AsyncTaskChain; //////////////////////////////////////////////////////////////////// class EXPCL_PANDA_EVENT AsyncTask : public TypedReferenceCount, public Namable { public: - INLINE AsyncTask(const string &name = string()); + AsyncTask(const string &name = string()); virtual ~AsyncTask(); ALLOC_DELETED_CHAIN(AsyncTask); @@ -96,9 +96,14 @@ PUBLISHED: INLINE PyObject *get_python_object() const; #endif // HAVE_PYTHON + INLINE double get_dt() const; + INLINE double get_max_dt() const; + INLINE double get_average_dt() const; + virtual void output(ostream &out) const; protected: + DoneStatus unlock_and_do_task(); virtual DoneStatus do_task(); protected: @@ -115,6 +120,11 @@ protected: AsyncTaskManager *_manager; AsyncTaskChain *_chain; + double _dt; + double _max_dt; + double _total_dt; + int _num_frames; + private: #ifdef HAVE_PYTHON PyObject *_python_object; diff --git a/panda/src/event/asyncTaskChain.cxx b/panda/src/event/asyncTaskChain.cxx index 109a6c1cbc..f4c9ec61cd 100644 --- a/panda/src/event/asyncTaskChain.cxx +++ b/panda/src/event/asyncTaskChain.cxx @@ -24,6 +24,7 @@ #include "pStatTimer.h" #include "clockObject.h" #include +#include // For sprintf/snprintf TypeHandle AsyncTaskChain::_type_handle; @@ -507,15 +508,7 @@ service_one_task(AsyncTaskChain::AsyncTaskChainThread *thread) { task->_state = AsyncTask::S_servicing; task->_servicing_thread = thread; - // Now release the chain lock while we actually service the - // task. - _manager->_lock.release(); - AsyncTask::DoneStatus ds = task->do_task(); - - // Now we have to re-acquire the chain lock, so we can put the - // task back on the queue (and so we can return with the lock - // still held). - _manager->_lock.lock(); + AsyncTask::DoneStatus ds = task->unlock_and_do_task(); if (thread != (AsyncTaskChain::AsyncTaskChainThread *)NULL) { thread->_servicing = NULL; @@ -801,8 +794,28 @@ do_poll() { //////////////////////////////////////////////////////////////////// void AsyncTaskChain:: do_write(ostream &out, int indent_level) const { - indent(out, indent_level) - << get_type() << " " << get_name() << "\n"; + if (has_name()) { + indent(out, indent_level) + << get_name() << "\n"; + } + + static const size_t buffer_size = 1024; + char buffer[buffer_size]; + sprintf(buffer, " %-32s %8s %8s %8s %8s %6s", + "Task", + "sleep(s)", + "dt(ms)", "avg", "max", + "sort"); + nassertv(strlen(buffer) < buffer_size); + + indent(out, indent_level) + << buffer << "\n"; + + indent(out, indent_level); + for (int i = 0; i < 32+8+8+8+8+6+7; ++i) { + out << '-'; + } + out << "\n"; // Collect a list of all active tasks, then sort them into order for // output. @@ -818,55 +831,78 @@ do_write(ostream &out, int indent_level) const { } } - if (!tasks.empty()) { - indent(out, indent_level + 2) - << "Active tasks:\n"; + sort(tasks.begin(), tasks.end(), AsyncTaskSortPriority()); - sort(tasks.begin(), tasks.end(), AsyncTaskSortPriority()); + double now = _manager->_clock->get_frame_time(); - // Since AsyncTaskSortPriority() sorts backwards (because of STL's - // push_heap semantics), we go through the task list in reverse - // order to print them forwards. - TaskHeap::reverse_iterator ti; - int current_sort = tasks.back()->get_sort() - 1; - for (ti = tasks.rbegin(); ti != tasks.rend(); ++ti) { - AsyncTask *task = (*ti); - if (task->get_sort() != current_sort) { - current_sort = task->get_sort(); - indent(out, indent_level + 2) - << "sort = " << current_sort << "\n"; - } - if (task->_state == AsyncTask::S_servicing) { - indent(out, indent_level + 3) - << "*" << *task << "\n"; - } else { - indent(out, indent_level + 4) - << *task << "\n"; - } - } + // Since AsyncTaskSortPriority() sorts backwards (because of STL's + // push_heap semantics), we go through the task list in reverse + // order to print them forwards. + TaskHeap::reverse_iterator ti; + int current_sort = tasks.back()->get_sort() - 1; + for (ti = tasks.rbegin(); ti != tasks.rend(); ++ti) { + AsyncTask *task = (*ti); + write_task_line(out, indent_level, task, now); } - if (!_sleeping.empty()) { - indent(out, indent_level + 2) - << "Sleeping tasks:\n"; - double now = _manager->_clock->get_frame_time(); - - // Instead of iterating through the _sleeping list in heap order, - // copy it and then use repeated pops to get it out in sorted - // order, for the user's satisfaction. - TaskHeap sleeping = _sleeping; - while (!sleeping.empty()) { - PT(AsyncTask) task = sleeping.front(); - pop_heap(sleeping.begin(), sleeping.end(), AsyncTaskSortWakeTime()); - sleeping.pop_back(); - - indent(out, indent_level + 4) - << task->get_wake_time() - now << "s: " - << *task << "\n"; - } + // Instead of iterating through the _sleeping list in heap order, + // copy it and then use repeated pops to get it out in sorted + // order, for the user's satisfaction. + TaskHeap sleeping = _sleeping; + while (!sleeping.empty()) { + PT(AsyncTask) task = sleeping.front(); + pop_heap(sleeping.begin(), sleeping.end(), AsyncTaskSortWakeTime()); + sleeping.pop_back(); + + write_task_line(out, indent_level, task, now); } } +//////////////////////////////////////////////////////////////////// +// Function: AsyncTaskChain::write_task_line +// Access: Protected +// Description: Writes a single line for a task in the report +// generated by do_write(). Assumes the lock is already +// held. +//////////////////////////////////////////////////////////////////// +void AsyncTaskChain:: +write_task_line(ostream &out, int indent_level, AsyncTask *task, double now) const { + char servicing_flag = ' '; + if (task->_state == AsyncTask::S_servicing) { + servicing_flag = '*'; + } else if (task->_state == AsyncTask::S_servicing_removed) { + servicing_flag = '-'; + } + + static const size_t buffer_size = 1024; + char buffer[buffer_size]; + + if (task->_state == AsyncTask::S_sleeping) { + // For sleeping tasks, include the wake time, as an elapsed time + // in seconds. + string name = task->get_name().substr(0, 32); + sprintf(buffer, "%c%-32s %8.1f %8.1f %8.1f %8.1f %6d", + servicing_flag, name.c_str(), + task->_wake_time - now, + task->_dt * 1000.0, task->get_average_dt() * 1000.0, + task->_max_dt * 1000.0, + task->_sort); + } else { + // For active tasks, don't include a wake time. This means we + // have more space for the name. + string name = task->get_name().substr(0, 41); + sprintf(buffer, "%c%-41s %8.1f %8.1f %8.1f %6d", + servicing_flag, name.c_str(), + task->_dt * 1000.0, task->get_average_dt() * 1000.0, + task->_max_dt * 1000.0, + task->_sort); + } + nassertv(strlen(buffer) < buffer_size); + + indent(out, indent_level) + << buffer << "\n"; +} + //////////////////////////////////////////////////////////////////// // Function: AsyncTaskChain::AsyncTaskChainThread::Constructor // Access: Public diff --git a/panda/src/event/asyncTaskChain.h b/panda/src/event/asyncTaskChain.h index b6aca289e3..d01d2655b8 100644 --- a/panda/src/event/asyncTaskChain.h +++ b/panda/src/event/asyncTaskChain.h @@ -108,6 +108,8 @@ protected: void do_poll(); void do_write(ostream &out, int indent_level) const; + void write_task_line(ostream &out, int indent_level, AsyncTask *task, double now) const; + protected: class AsyncTaskChainThread : public Thread { public: diff --git a/panda/src/event/asyncTaskManager.cxx b/panda/src/event/asyncTaskManager.cxx index 4a3d8b9a65..85a809b6b4 100644 --- a/panda/src/event/asyncTaskManager.cxx +++ b/panda/src/event/asyncTaskManager.cxx @@ -37,6 +37,8 @@ AsyncTaskManager(const string &name) : Namable(name), _clock(ClockObject::get_global_clock()) { + // Make a default task chain. + do_make_task_chain(""); } ////////////////////////////////////////////////////////////////////