[PATCH] glretrace: Add feature to loop over a frame or call range

Jon Ashburn jon at lunarg.com
Tue Apr 22 11:53:10 PDT 2014


Existing looping is over the last frame (or partial frame)  in a trace. This
adds the ability to specify --framerange  and --callrange options to loop
over any frame or call range within the trace.
---
 retrace/retrace_main.cpp | 337 ++++++++++++++++++++++++++++++++---------------
 1 file changed, 233 insertions(+), 104 deletions(-)

diff --git a/retrace/retrace_main.cpp b/retrace/retrace_main.cpp
index 6327f1b..3dbf3b7 100644
--- a/retrace/retrace_main.cpp
+++ b/retrace/retrace_main.cpp
@@ -92,6 +92,14 @@ unsigned callNo = 0;
 unsigned loopIter = 1;
 static trace::ParseBookmark lastFrameStart;
 
+// call and frame range for looping
+bool callrange = false;
+unsigned rangeStartCallNum = 0;
+unsigned rangeEndCallNum = 0;
+bool framerange = false;
+unsigned rangeStartFrameNum = 0;
+unsigned rangeEndFrameNum = 0;
+
 void
 frameComplete(trace::Call &call) {
     ++frameNo;
@@ -158,6 +166,10 @@ static bool
 doneLoopSetup(trace::Call *call, int frameNum)
 {
     static bool lastCallEndFrame=false;
+    /*
+     * If looping last frame  then done when no more calls or end of frame.
+     * Otherwise done  based on framerange or callrange */
+
     if (!call)
         return true;
 
@@ -172,100 +184,187 @@ doneLoopSetup(trace::Call *call, int frameNum)
         else
             return false;
     }
+    if (framerange  && frameNum == rangeEndFrameNum  && (call->flags & trace::CALL_FLAG_END_FRAME))
+        lastCallEndFrame = true;
+
+    if (callrange && call->no == rangeEndCallNum)
+        lastCallEndFrame = true;
 
     return false;
 }
 
+
+/**
+ * Retrace one call.
+ *
+ * Take snapshots before/after retracing (as appropriate) and dispatch it to
+ * the respective handler.
+ */
 static void
-loopingFrameReplay( retrace::Retracer rt) {
-    trace::Call *call;
+retraceCall(trace::Call *call) {
+    bool swapRenderTarget = call->flags &
+        trace::CALL_FLAG_SWAP_RENDERTARGET;
+    bool doSnapshot = snapshotFrequency.contains(*call);
+
+    // For calls which cause rendertargets to be swaped, we take the
+    // snapshot _before_ swapping the rendertargets.
+    if (doSnapshot && swapRenderTarget) {
+        if (call->flags & trace::CALL_FLAG_END_FRAME) {
+            // For swapbuffers/presents we still use this
+            // call number, spite not have been executed yet.
+            takeSnapshot(call->no);
+        } else {
+            // Whereas for ordinate fbo/rendertarget changes we
+            // use the previous call's number.
+            takeSnapshot(call->no - 1);
+        }
+    }
+
+    callNo = call->no;
+    retracer.retrace(*call);
 
-    // keep track of start and end time
+    if (doSnapshot && !swapRenderTarget)
+        takeSnapshot(call->no);
+
+    if (call->no >= dumpStateCallNo &&
+        dumper->dumpState(std::cout)) {
+        exit(0);
+    }
+}
+
+static void
+doLooping(std::vector<trace::Call*> & savedCalls, retrace::Retracer rt, int loopNum)
+{
+    int i;
+    if (retrace::verbosity >= 1) {
+        std::cout << "loop: " << loopNum << "\n";
+    }
+    for (i = 0; i < savedCalls.size(); i++) {
+        trace::Call *call2 = savedCalls[i];
+        rt.retrace(*call2);
+        if (retrace::verbosity >= 1) {
+            std::cout << "retraced call: " << call2->no << ":" << call2->name() << "\n";
+        }
+    }
+}
+
+static void
+loopingFrameReplay( trace::Call *call, retrace::Retracer rt)
+{
+
+    // keep track of start and end time for setup  and actual looping
     long long setupStartTime, setupEndTime;
     long long framePerfStart, framePerfEnd;
+    float timeInterval;
+    char timestr[15];
 
-    // holds saved call pointers and index into array
+    /*
+     * Holds saved call pointers and index into array.
+     * If only looping one time this array is not used
+     */
     std::vector<trace::Call*> savedCalls;
 
-    int bucketNum = frameNo;
-    int startBucketNum = bucketNum;
-    const char *bucketName = "Frame ";
-
-    if (loopIter)
-        std::cout << "Looping " << loopIter << " times on " << bucketName << bucketNum << "\n";
-    else
-        std::cout << "Looping continuously on " << bucketName << bucketNum << "\n";
+    int bucketNum = (framerange) ? rangeStartFrameNum : frameNo;
+    int startBucketNum;
     int startCallNum = 0, endCallNum = 0;
 
+    if (loopOnFinish) {
+        call = parser.parse_call();
+        if (bucketNum != 0)
+            bucketNum -= 1;
+    }
+    startBucketNum = bucketNum;
+    
+    if (!call) {
+        std::cerr << "Skipping Looping invalid call from parse_call()\n";
+        return;
+    }
+    if (framerange && (frameNo < rangeStartFrameNum || frameNo > rangeEndFrameNum)) {
+        std::cerr << "Skipping Looping invalid frame range given\n";
+        return;
+    }
+
+    if (loopIter > 1)
+        std::cout << "Looping " << loopIter << " times on starting frame " << bucketNum << "\n";
+    else if (loopIter == 0)
+        std::cout << "Looping continuously on frame " << bucketNum << "\n";
+
     setupStartTime = os::getTime();
     setupEndTime = setupStartTime;
 
-    //frame setup
-    call = parser.parse_call();
     startCallNum = call->no;
-    while( !doneLoopSetup(call, bucketNum) ) {
-        // save the call pointer
-        savedCalls.push_back(call);
-        if (retrace::verbosity >= 1) {
-            std::cout << "saving call number "  <<  call->no << "\n";
+    if (callrange && (startCallNum != rangeStartCallNum)) {
+        std::cerr << "Skipping Looping invalid call range given\n";
+        return;
+    }
+
+    /*
+     * frame setup saving the API calls, or if loop iterations == 1 
+     * then the one loop is executed in this while loop
+     */
+    while ( !doneLoopSetup(call, bucketNum) ) {
+        endCallNum = call->no;
+        if (loopIter != 1) {
+            // save the call pointer
+            savedCalls.push_back(call);
+            if (retrace::verbosity >= 1) {
+                std::cout << "saving call number "  <<  call->no << "\n";
+            }
+        } else {
+            // execute the call
+            retraceCall(call);
+            delete call;
         }
 
         if ((call->flags & trace::CALL_FLAG_END_FRAME) ) {
             bucketNum = bucketNum + 1;
         }
-        endCallNum = call->no;
         call = parser.parse_call();
     }
     setupEndTime = os::getTime();
 
-    // do the looping on saved calls
-    int i, j = 0;
-    while (loopIter == 0) {
-        // loop continuously
-        if (retrace::verbosity >= 1) {
-            std::cout << "loop: " << j << "\n";
+    if (loopIter != 1) {
+        // do the looping on saved calls
+        int j = 0;
+        while (loopIter == 0) {
+            // loop continuously
+            doLooping(savedCalls, rt, j);
+            j++;
         }
-        for (i = 0; i < savedCalls.size(); i++) {
-            trace::Call *call2 = savedCalls[i];
-            rt.retrace(*call2);
-            if (retrace::verbosity >= 1) {
-                std::cout << "retraced call: " << call2->no << ":" << call2->name() << "\n";
-            }
-        }
-        j++;
-    }
-    framePerfStart = os::getTime();
-    for (j = 0; j < loopIter; j++) {
-        if (retrace::verbosity >= 1) {
-            std::cout << "loop: " << j << "\n";
-        }
-        for (i = 0; i < savedCalls.size(); i++) {
-            trace::Call *call2 = savedCalls[i];
-            rt.retrace(*call2);
-            if (retrace::verbosity >= 1) {
-                std::cout << "retraced call: " << call2->no << ":" << call2->name() << "\n";
-            }
+        framePerfStart = os::getTime();
+        for (j = 0; j < loopIter; j++) {
+            doLooping(savedCalls, rt, j);
         }
-    }
-    framePerfEnd = os::getTime();
+        framePerfEnd = os::getTime();
 
-    // print out the statistics
-    char timestr[15];
-    float timeInterval =
+        // print out the setup statistics
+        timeInterval =
             (setupEndTime - setupStartTime) * (1.0 / os::timeFrequency);
-    if (timeInterval > 0.0) {
-        std::cout << "Setup call range (" << startCallNum << "-" << endCallNum << "):\n";
-        sprintf(timestr, "%.9f", timeInterval);
-        std::cout << "   total time was   " << timestr << " secs\n";
+        if (timeInterval > 0.0) {
+            std::cout << "Setup call range (" << startCallNum << "-" <<
+                endCallNum << "):\n";
+            sprintf(timestr, "%.9f", timeInterval);
+            std::cout << "   total time was   " << timestr << " secs\n";
+        }
+    } else {
+        framePerfStart = setupStartTime;
+        framePerfEnd = setupEndTime;
     }
+
+    // print out the looping statistics
     timeInterval =
             (framePerfEnd - framePerfStart) * (1.0 / os::timeFrequency);
-    std::cout << bucketName << " (" << startBucketNum << "-" << bucketNum << ") call range (" << startCallNum << "-" << endCallNum << "):\n";
+    if (framerange && bucketNum > rangeEndFrameNum)
+        bucketNum = rangeEndFrameNum;
+    if (loopOnFinish)
+        bucketNum = startBucketNum;
+    float numFrames =  (float) (1 + bucketNum - startBucketNum);
+    std::cout << "Frame (" << startBucketNum << "-" << bucketNum << ") call range (" << startCallNum << "-" << endCallNum << "):\n";
     sprintf(timestr, "%.9f", timeInterval);
     std::cout << "   total time was   " << timestr << " secs\n";
-    sprintf(timestr, "%.9f", (timeInterval / (1.0 * loopIter)));
-    std::cout << "   average time was " << timestr << " secs\n";
-    sprintf(timestr, "%.9f", (((1.0) * loopIter) / timeInterval));
+    sprintf(timestr, "%.9f", (timeInterval / (numFrames * (float) loopIter)));
+    std::cout << "   average time per frame was " << timestr << " secs\n";
+    sprintf(timestr, "%.9f", ((numFrames * (float) loopIter) / timeInterval));
     std::cout << "   fps was " << timestr << " frames per sec\n";
 
     // free the saved calls
@@ -278,45 +377,41 @@ loopingFrameReplay( retrace::Retracer rt) {
     flushRendering();
 }
 
+static bool
+beforeLooping(trace::Call *call)
+{
+    static bool lastCallEndFrame=false;
+    
+    if (loopOnFinish)
+        return (call != NULL);
+    /*
+     * If looping and NO callrange or framerange defined then replay till
+     * the end of trace, looping code keeps a bookmark of the start of last
+     * frame for looping.
+     */
 
-/**
- * Retrace one call.
- *
- * Take snapshots before/after retracing (as appropriate) and dispatch it to
- * the respective handler.
- */
-static void
-retraceCall(trace::Call *call) {
-    bool swapRenderTarget = call->flags &
-        trace::CALL_FLAG_SWAP_RENDERTARGET;
-    bool doSnapshot = snapshotFrequency.contains(*call);
+    if (!framerange  &&  !callrange)
+        return (call != NULL);
 
-    // For calls which cause rendertargets to be swaped, we take the
-    // snapshot _before_ swapping the rendertargets.
-    if (doSnapshot && swapRenderTarget) {
-        if (call->flags & trace::CALL_FLAG_END_FRAME) {
-            // For swapbuffers/presents we still use this
-            // call number, spite not have been executed yet.
-            takeSnapshot(call->no);
-        } else {
-            // Whereas for ordinate fbo/rendertarget changes we
-            // use the previous call's number.
-            takeSnapshot(call->no - 1);
-        }
+    if (!call)
+        return false;
+    /*
+     * If callrange or framerange defined then transition to looping replay
+     * when get start of call or frame range
+     */
+    if (callrange && call->no >= rangeStartCallNum) {
+        return false;
     }
-
-    callNo = call->no;
-    retracer.retrace(*call);
-
-    if (doSnapshot && !swapRenderTarget)
-        takeSnapshot(call->no);
-
-    if (call->no >= dumpStateCallNo &&
-        dumper->dumpState(std::cout)) {
-        exit(0);
+    if (framerange) {
+        if (rangeStartFrameNum == 0 || lastCallEndFrame)
+            return false;
+        if (frameNo+1 == rangeStartFrameNum &&
+            (call->flags & trace::CALL_FLAG_END_FRAME))
+                lastCallEndFrame = true;
     }
-}
 
+    return true;
+}
 
 class RelayRunner;
 
@@ -446,7 +541,7 @@ public:
         trace::ParseBookmark frameStart;
 
         /* Consume successive calls for this thread. */
-        do {
+        while (beforeLooping(call) && call->thread_id == leg) {
             bool callEndsFrame = false;
 
             assert(call);
@@ -466,17 +561,16 @@ public:
             else if (callEndsFrame)
                 lastFrameStart = frameStart;
 
-        } while (call && call->thread_id == leg);
+        }
 
-        if (call) {
+        if (call && call->thread_id != leg) {
             /* Pass the baton */
-            assert(call->thread_id != leg);
             flushRendering();
             race->passBaton(call);
         } else {
             /* Reached the finish line */
-            if (loopOnFinish)
-                loopingFrameReplay(retracer);
+            if (loopOnFinish || callrange || framerange)
+                loopingFrameReplay(call, retracer);
             if (0) std::cerr << "finished on leg " << leg << "\n";
             if (leg) {
                 /* Notify the fore runner */
@@ -650,7 +744,7 @@ mainLoop() {
 
         trace::Call *call;
         call = parser.parse_call();
-        while (call) {
+        while (beforeLooping(call)) {
             bool callEndsFrame = false;
 
             if (loopOnFinish && call->flags & trace::CALL_FLAG_END_FRAME) {
@@ -665,8 +759,8 @@ mainLoop() {
             else if (callEndsFrame)
                 lastFrameStart = frameStart;
         };
-        if (loopOnFinish)
-            loopingFrameReplay( retracer);
+        if (loopOnFinish || callrange || framerange)
+            loopingFrameReplay( call, retracer);
 
     } else {
         RelayRace race;
@@ -719,7 +813,9 @@ usage(const char *argv0) {
         "  -v, --verbose           increase output verbosity\n"
         "  -D, --dump-state=CALL   dump state at specific call no\n"
         "  -w, --wait              waitOnFinish on final frame\n"
-        "      --loop=N            loop N times (N=0 continuously) replaying final frame.\n"
+        "      --loop=N            loop N times (N=0 continuously) replaying (call/frame range or final) frame.\n"
+        "      --callrange=CALLSET loop on CALLSET (contiguous)\n"
+        "      --framerange=RANGESET loop on RANGESET of frames (contiguous)\n"
         "      --singlethread      use a single thread to replay command stream\n";
 }
 
@@ -736,6 +832,8 @@ enum {
     SB_OPT,
     SNAPSHOT_FORMAT_OPT,
     LOOP_OPT,
+    CALLRANGE_OPT,
+    FRAMERANGE_OPT,
     SINGLETHREAD_OPT
 };
 
@@ -764,6 +862,8 @@ longOptions[] = {
     {"verbose", no_argument, 0, 'v'},
     {"wait", no_argument, 0, 'w'},
     {"loop", required_argument, 0, LOOP_OPT},
+    {"callrange", required_argument, 0, CALLRANGE_OPT},
+    {"framerange", required_argument, 0, FRAMERANGE_OPT},
     {"singlethread", no_argument, 0, SINGLETHREAD_OPT},
     {0, 0, 0, 0}
 };
@@ -880,9 +980,38 @@ int main(int argc, char **argv)
         case 'w':
             waitOnFinish = true;
             break;
+        case CALLRANGE_OPT:
+            callrange = true;
+            framerange = false;
+            loopOnFinish = false;
+            retrace::debug = false;
+            static trace::CallSet callSet = trace::CallSet();
+            callSet.merge(optarg);
+            rangeStartCallNum = callSet.getFirst();
+            rangeEndCallNum = callSet.getLast();
+            if (rangeStartCallNum > rangeEndCallNum) {
+                callrange = false;
+                std::cerr << "error bad call range values\n";
+            }
+            break;
+        case FRAMERANGE_OPT:
+            framerange = true;
+            callrange = false;
+            loopOnFinish = false;
+            retrace::debug = false;
+            static trace::CallSet frameSet = trace::CallSet();
+            frameSet.merge(optarg);
+            rangeStartFrameNum = frameSet.getFirst();
+            rangeEndFrameNum = frameSet.getLast();
+            if (rangeStartFrameNum > rangeEndFrameNum) {
+                framerange = false;
+                std::cerr << "error bad frame range values\n";
+            }
+            break;
         case LOOP_OPT:
             loopIter = atoi(optarg);
-            loopOnFinish = true;
+            if (!callrange && !framerange)
+                loopOnFinish = true;
             break;
         case PGPU_OPT:
             retrace::debug = 0;
-- 
1.8.1.2



More information about the apitrace mailing list