diff --git a/direct/src/showbase/ProfileSession.py b/direct/src/showbase/ProfileSession.py index e90d129b38..4ee0a70ea2 100755 --- a/direct/src/showbase/ProfileSession.py +++ b/direct/src/showbase/ProfileSession.py @@ -306,6 +306,8 @@ class ProfileSession: if statsChanged: self._stats.strip_dirs() + # throw out any cached result strings + self._resultCache = {} return statsChanged @@ -326,10 +328,7 @@ class ProfileSession: if totalTime is Default: totalTime = self._totalTime - statsChanged = self._compileStats() - if statsChanged: - # throw out any cached result strings - self._resultCache = {} + self._compileStats() if totalTime is None: totalTime = self._stats.total_tt diff --git a/direct/src/task/FrameProfiler.py b/direct/src/task/FrameProfiler.py index 179878baa2..3283068f76 100755 --- a/direct/src/task/FrameProfiler.py +++ b/direct/src/task/FrameProfiler.py @@ -14,6 +14,8 @@ class FrameProfiler: Hour = FrameProfiler.Hour # how long to wait between frame profiles self._period = 2 * FrameProfiler.Minute + if config.GetBool('frame-profiler-debug', 0): + self._period = 1 * FrameProfiler.Minute # used to prevent profile from being taken exactly every 'period' seconds self._jitterMagnitude = self._period * .75 # when to log output @@ -24,6 +26,12 @@ class FrameProfiler: 12 * FrameProfiler.Hour, 1 * FrameProfiler.Day, ] # day schedule proceeds as 1, 2, 4, 8 days, etc. + if config.GetBool('frame-profiler-debug', 0): + self._logSchedule = [ 1 * FrameProfiler.Minute, + 4 * FrameProfiler.Minute, + 12 * FrameProfiler.Minute, + 24 * FrameProfiler.Minute, + ] for t in self._logSchedule: assert isInteger(t) # make sure the period is evenly divisible into each element of the log schedule @@ -47,9 +55,10 @@ class FrameProfiler: self._profileCounter = 0 self._jitter = None self._period2aggregateProfile = {} - self._lastSession = None + self._id2session = {} + self._id2task = {} # don't profile process startup - self._task = taskMgr.doMethodLater(self._period, self._startProfiling, + self._task = taskMgr.doMethodLater(self._period, self._scheduleNextProfileDoLater, 'FrameProfilerStart-%s' % serialNum()) else: self._task.remove() @@ -57,12 +66,15 @@ class FrameProfiler: for session in self._period2aggregateProfile.itervalues: session.release() del self._period2aggregateProfile - if self._lastSession: - self._lastSession.release() - del self._lastSession + for task in self._id2task.itervalues(): + task.remove() + del self._id2task + for session in self._id2session.itervalues(): + session.release() + del self._id2session self.notify.info('frame profiler stopped') - def _startProfiling(self, task): + def _scheduleNextProfileDoLater(self, task): self._scheduleNextProfile() return task.done @@ -83,30 +95,41 @@ class FrameProfiler: jitter = None self._jitter = jitter - self._lastSession = taskMgr.getProfileSession('FrameProfile-%s' % serialNum()) - taskMgr.profileFrames(num=1, session=self._lastSession) + sessionId = serialNum() + session = taskMgr.getProfileSession('FrameProfile-%s' % sessionId) + self._id2session[sessionId] = session + taskMgr.profileFrames(num=1, session=session, callback=Functor( + self._analyzeResults, sessionId)) + # schedule the next profile delay = max(time - globalClock.getFrameTime(), 0.) - self._task = taskMgr.doMethodLater(delay, self._frameProfileTask, + self._task = taskMgr.doMethodLater(delay, self._scheduleNextProfileDoLater, 'FrameProfiler-%s' % serialNum()) - def _frameProfileTask(self, task): + def _analyzeResults(self, sessionId): + # do the analysis in a task 1) to separate the processing from the profiled frame, + # and 2) to get the processing to show up in a named task instead of in the taskMgr + self._id2task[sessionId] = taskMgr.add( + Functor(self._doAnalysis, sessionId), 'FrameProfilerAnalysis-%s' % sessionId) + + def _doAnalysis(self, sessionId, task): p2ap = self._period2aggregateProfile - if self._lastSession: - if self._lastSession.profileSucceeded(): - # always add this profile to the first aggregated profile - period = self._logSchedule[0] - if period not in self._period2aggregateProfile: - p2ap[period] = self._lastSession.getReference() - else: - p2ap[period].aggregate(self._lastSession) - else: - self.notify.warning('frame profile did not succeed') + self._id2task.pop(sessionId) + session = self._id2session.pop(sessionId) - # always release the last-recorded profile - self._lastSession.release() - self._lastSession = None + if session.profileSucceeded(): + # always add this profile to the first aggregated profile + period = self._logSchedule[0] + if period not in self._period2aggregateProfile: + p2ap[period] = session.getReference() + else: + p2ap[period].aggregate(session) + else: + self.notify.warning('frame profile did not succeed') + + session.release() + session = None # log profiles when it's time, and aggregate them upwards into the # next-longer profile @@ -138,5 +161,5 @@ class FrameProfiler: # periods are multiples of this one break - self._scheduleNextProfile() return task.done + diff --git a/direct/src/task/TaskNew.py b/direct/src/task/TaskNew.py index f0b3300ca6..a4307d4de0 100644 --- a/direct/src/task/TaskNew.py +++ b/direct/src/task/TaskNew.py @@ -456,12 +456,15 @@ class TaskManager: while self.running: try: if len(self._frameProfileQueue): - numFrames, session = self._frameProfileQueue.pop() + numFrames, session, callback = self._frameProfileQueue.pop() def _profileFunc(numFrames=numFrames): self._doProfiledFrames(numFrames) session.setFunc(_profileFunc) session.run() _profileFunc = None + if callback: + callback() + session.release() else: self.step() except KeyboardInterrupt: @@ -546,12 +549,14 @@ class TaskManager: name = 'taskMgrFrameProfile' return ProfileSession(name) - def profileFrames(self, num=None, session=None): + def profileFrames(self, num=None, session=None, callback=None): if num is None: num = 1 if session is None: session = self.getProfileSession() - self._frameProfileQueue.push((num, session)) + # make sure the profile session doesn't get destroyed before we're done with it + session.acquire() + self._frameProfileQueue.push((num, session, callback)) def _doProfiledFrames(self, numFrames): for i in xrange(numFrames): diff --git a/direct/src/task/TaskOrig.py b/direct/src/task/TaskOrig.py index 3a3cb62fc9..93c1859945 100644 --- a/direct/src/task/TaskOrig.py +++ b/direct/src/task/TaskOrig.py @@ -1030,12 +1030,14 @@ class TaskManager: name = 'taskMgrFrameProfile' return ProfileSession(name) - def profileFrames(self, num=None, session=None): + def profileFrames(self, num=None, session=None, callback=None): if num is None: num = 1 if session is None: session = self.getProfileSession() - self._frameProfileQueue.push((num, session)) + # make sure the profile session doesn't get destroyed before we're done with it + session.acquire() + self._frameProfileQueue.push((num, session, callback)) # in the event we want to do frame time managment.. this is the function to @@ -1241,12 +1243,15 @@ class TaskManager: while self.running: try: if len(self._frameProfileQueue): - numFrames, session = self._frameProfileQueue.pop() + numFrames, session, callback = self._frameProfileQueue.pop() def _profileFunc(numFrames=numFrames): self._doProfiledFrames(numFrames) session.setFunc(_profileFunc) session.run() _profileFunc = None + if callback: + callback() + session.release() else: self.step() except KeyboardInterrupt: