]> git.cworth.org Git - apitrace/blob - retrace/retrace_main.cpp
Merge branch 'master' into dxva
[apitrace] / retrace / retrace_main.cpp
1 /**************************************************************************
2  *
3  * Copyright 2011 Jose Fonseca
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25
26
27 #include <string.h>
28 #include <iostream>
29
30 #include "os_binary.hpp"
31 #include "os_time.hpp"
32 #include "os_thread.hpp"
33 #include "image.hpp"
34 #include "trace_callset.hpp"
35 #include "trace_dump.hpp"
36 #include "retrace.hpp"
37
38
39 static bool waitOnFinish = false;
40
41 static const char *comparePrefix = NULL;
42 static const char *snapshotPrefix = NULL;
43 static trace::CallSet snapshotFrequency;
44 static trace::CallSet compareFrequency;
45
46 static unsigned dumpStateCallNo = ~0;
47
48 retrace::Retracer retracer;
49
50
51 namespace retrace {
52
53
54 trace::Parser parser;
55 trace::Profiler profiler;
56
57
58 int verbosity = 0;
59 bool debug = true;
60 bool dumpingState = false;
61
62 bool doubleBuffer = true;
63 bool coreProfile = false;
64
65 bool profiling = false;
66 bool profilingGpuTimes = false;
67 bool profilingCpuTimes = false;
68 bool profilingPixelsDrawn = false;
69
70 unsigned frameNo = 0;
71 unsigned callNo = 0;
72
73
74 void
75 frameComplete(trace::Call &call) {
76     ++frameNo;
77 }
78
79
80 /**
81  * Take/compare snapshots.
82  */
83 static void
84 takeSnapshot(unsigned call_no) {
85     assert(snapshotPrefix || comparePrefix);
86
87     image::Image *ref = NULL;
88
89     if (comparePrefix) {
90         os::String filename = os::String::format("%s%010u.png", comparePrefix, call_no);
91         ref = image::readPNG(filename);
92         if (!ref) {
93             return;
94         }
95         if (retrace::verbosity >= 0) {
96             std::cout << "Read " << filename << "\n";
97         }
98     }
99
100     image::Image *src = getSnapshot();
101     if (!src) {
102         return;
103     }
104
105     if (snapshotPrefix) {
106         if (snapshotPrefix[0] == '-' && snapshotPrefix[1] == 0) {
107             char comment[21];
108             snprintf(comment, sizeof comment, "%u", call_no);
109             src->writePNM(std::cout, comment);
110         } else {
111             os::String filename = os::String::format("%s%010u.png", snapshotPrefix, call_no);
112             if (src->writePNG(filename) && retrace::verbosity >= 0) {
113                 std::cout << "Wrote " << filename << "\n";
114             }
115         }
116     }
117
118     if (ref) {
119         std::cout << "Snapshot " << call_no << " average precision of " << src->compare(*ref) << " bits\n";
120         delete ref;
121     }
122
123     delete src;
124
125     return;
126 }
127
128
129 /**
130  * Retrace one call.
131  *
132  * Take snapshots before/after retracing (as appropriate) and dispatch it to
133  * the respective handler.
134  */
135 static void
136 retraceCall(trace::Call *call) {
137     bool swapRenderTarget = call->flags &
138         trace::CALL_FLAG_SWAP_RENDERTARGET;
139     bool doSnapshot = snapshotFrequency.contains(*call) ||
140         compareFrequency.contains(*call);
141
142     // For calls which cause rendertargets to be swaped, we take the
143     // snapshot _before_ swapping the rendertargets.
144     if (doSnapshot && swapRenderTarget) {
145         if (call->flags & trace::CALL_FLAG_END_FRAME) {
146             // For swapbuffers/presents we still use this
147             // call number, spite not have been executed yet.
148             takeSnapshot(call->no);
149         } else {
150             // Whereas for ordinate fbo/rendertarget changes we
151             // use the previous call's number.
152             takeSnapshot(call->no - 1);
153         }
154     }
155
156     callNo = call->no;
157     retracer.retrace(*call);
158
159     if (doSnapshot && !swapRenderTarget)
160         takeSnapshot(call->no);
161
162     if (call->no >= dumpStateCallNo &&
163         dumpState(std::cout)) {
164         exit(0);
165     }
166 }
167
168
169 class RelayRunner;
170
171
172 /**
173  * Implement multi-threading by mimicking a relay race.
174  */
175 class RelayRace
176 {
177 private:
178     /**
179      * Runners indexed by the leg they run (i.e, the thread_ids from the
180      * trace).
181      */
182     std::vector<RelayRunner*> runners;
183
184 public:
185     RelayRace();
186
187     ~RelayRace();
188
189     RelayRunner *
190     getRunner(unsigned leg);
191
192     inline RelayRunner *
193     getForeRunner() {
194         return getRunner(0);
195     }
196
197     void
198     run(void);
199
200     void
201     passBaton(trace::Call *call);
202
203     void
204     finishLine();
205
206     void
207     stopRunners();
208 };
209
210
211 /**
212  * Each runner is a thread.
213  *
214  * The fore runner doesn't have its own thread, but instead uses the thread
215  * where the race started.
216  */
217 class RelayRunner
218 {
219 private:
220     friend class RelayRace;
221
222     RelayRace *race;
223
224     unsigned leg;
225     
226     os::mutex mutex;
227     os::condition_variable wake_cond;
228
229     /**
230      * There are protected by the mutex.
231      */
232     bool finished;
233     trace::Call *baton;
234
235     os::thread thread;
236
237     static void *
238     runnerThread(RelayRunner *_this);
239
240 public:
241     RelayRunner(RelayRace *race, unsigned _leg) :
242         race(race),
243         leg(_leg),
244         finished(false),
245         baton(0)
246     {
247         /* The fore runner does not need a new thread */
248         if (leg) {
249             thread = os::thread(runnerThread, this);
250         }
251     }
252
253     /**
254      * Thread main loop.
255      */
256     void
257     runRace(void) {
258         os::unique_lock<os::mutex> lock(mutex);
259
260         while (1) {
261             while (!finished && !baton) {
262                 wake_cond.wait(lock);
263             }
264
265             if (finished) {
266                 break;
267             }
268
269             assert(baton);
270             trace::Call *call = baton;
271             baton = 0;
272
273             runLeg(call);
274         }
275
276         if (0) std::cerr << "leg " << leg << " actually finishing\n";
277
278         if (leg == 0) {
279             race->stopRunners();
280         }
281     }
282
283     /**
284      * Interpret successive calls.
285      */
286     void
287     runLeg(trace::Call *call) {
288         /* Consume successive calls for this thread. */
289         do {
290             assert(call);
291             assert(call->thread_id == leg);
292             retraceCall(call);
293             delete call;
294             call = parser.parse_call();
295         } while (call && call->thread_id == leg);
296
297         if (call) {
298             /* Pass the baton */
299             assert(call->thread_id != leg);
300             flushRendering();
301             race->passBaton(call);
302         } else {
303             /* Reached the finish line */
304             if (0) std::cerr << "finished on leg " << leg << "\n";
305             if (leg) {
306                 /* Notify the fore runner */
307                 race->finishLine();
308             } else {
309                 /* We are the fore runner */
310                 finished = true;
311             }
312         }
313     }
314
315     /**
316      * Called by other threads when relinquishing the baton.
317      */
318     void
319     receiveBaton(trace::Call *call) {
320         assert (call->thread_id == leg);
321
322         mutex.lock();
323         baton = call;
324         mutex.unlock();
325
326         wake_cond.signal();
327     }
328
329     /**
330      * Called by the fore runner when the race is over.
331      */
332     void
333     finishRace() {
334         if (0) std::cerr << "notify finish to leg " << leg << "\n";
335
336         mutex.lock();
337         finished = true;
338         mutex.unlock();
339
340         wake_cond.signal();
341     }
342 };
343
344
345 void *
346 RelayRunner::runnerThread(RelayRunner *_this) {
347     _this->runRace();
348     return 0;
349 }
350
351
352 RelayRace::RelayRace() {
353     runners.push_back(new RelayRunner(this, 0));
354 }
355
356
357 RelayRace::~RelayRace() {
358     assert(runners.size() >= 1);
359     std::vector<RelayRunner*>::const_iterator it;
360     for (it = runners.begin(); it != runners.end(); ++it) {
361         RelayRunner* runner = *it;
362         if (runner) {
363             delete runner;
364         }
365     }
366 }
367
368
369 /**
370  * Get (or instantiate) a runner for the specified leg.
371  */
372 RelayRunner *
373 RelayRace::getRunner(unsigned leg) {
374     RelayRunner *runner;
375
376     if (leg >= runners.size()) {
377         runners.resize(leg + 1);
378         runner = 0;
379     } else {
380         runner = runners[leg];
381     }
382     if (!runner) {
383         runner = new RelayRunner(this, leg);
384         runners[leg] = runner;
385     }
386     return runner;
387 }
388
389
390 /**
391  * Start the race.
392  */
393 void
394 RelayRace::run(void) {
395     trace::Call *call;
396     call = parser.parse_call();
397     if (!call) {
398         /* Nothing to do */
399         return;
400     }
401
402     RelayRunner *foreRunner = getForeRunner();
403     if (call->thread_id == 0) {
404         /* We are the forerunner thread, so no need to pass baton */
405         foreRunner->baton = call;
406     } else {
407         passBaton(call);
408     }
409
410     /* Start the forerunner thread */
411     foreRunner->runRace();
412 }
413
414
415 /**
416  * Pass the baton (i.e., the call) to the appropriate thread.
417  */
418 void
419 RelayRace::passBaton(trace::Call *call) {
420     if (0) std::cerr << "switching to thread " << call->thread_id << "\n";
421     RelayRunner *runner = getRunner(call->thread_id);
422     runner->receiveBaton(call);
423 }
424
425
426 /**
427  * Called when a runner other than the forerunner reaches the finish line.
428  *
429  * Only the fore runner can finish the race, so inform him that the race is
430  * finished.
431  */
432 void
433 RelayRace::finishLine(void) {
434     RelayRunner *foreRunner = getForeRunner();
435     foreRunner->finishRace();
436 }
437
438
439 /**
440  * Called by the fore runner after finish line to stop all other runners.
441  */
442 void
443 RelayRace::stopRunners(void) {
444     std::vector<RelayRunner*>::const_iterator it;
445     for (it = runners.begin() + 1; it != runners.end(); ++it) {
446         RelayRunner* runner = *it;
447         if (runner) {
448             runner->finishRace();
449         }
450     }
451 }
452
453
454 static void
455 mainLoop() {
456     addCallbacks(retracer);
457
458     long long startTime = 0; 
459     frameNo = 0;
460
461     startTime = os::getTime();
462
463     RelayRace race;
464     race.run();
465
466     long long endTime = os::getTime();
467     float timeInterval = (endTime - startTime) * (1.0 / os::timeFrequency);
468
469     if ((retrace::verbosity >= -1) || (retrace::profiling)) {
470         std::cout << 
471             "Rendered " << frameNo << " frames"
472             " in " <<  timeInterval << " secs,"
473             " average of " << (frameNo/timeInterval) << " fps\n";
474     }
475
476     if (waitOnFinish) {
477         waitForInput();
478     } else {
479         return;
480     }
481 }
482
483
484 } /* namespace retrace */
485
486
487 static void
488 usage(const char *argv0) {
489     std::cout << 
490         "Usage: " << argv0 << " [OPTION] TRACE [...]\n"
491         "Replay TRACE.\n"
492         "\n"
493         "  -b           benchmark mode (no error checking or warning messages)\n"
494         "  -pcpu        cpu profiling (cpu times per call)\n"
495         "  -pgpu        gpu profiling (gpu times per draw call)\n"
496         "  -ppd         pixels drawn profiling (pixels drawn per draw call)\n"
497         "  -c PREFIX    compare against snapshots\n"
498         "  -C CALLSET   calls to compare (default is every frame)\n"
499         "  -core        use core profile\n"
500         "  -db          use a double buffer visual (default)\n"
501         "  -sb          use a single buffer visual\n"
502         "  -s PREFIX    take snapshots; `-` for PNM stdout output\n"
503         "  -S CALLSET   calls to snapshot (default is every frame)\n"
504         "  -v           increase output verbosity\n"
505         "  -D CALLNO    dump state at specific call no\n"
506         "  -w           waitOnFinish on final frame\n";
507 }
508
509
510 extern "C"
511 int main(int argc, char **argv)
512 {
513     using namespace retrace;
514
515     assert(compareFrequency.empty());
516     assert(snapshotFrequency.empty());
517
518     int i;
519     for (i = 1; i < argc; ++i) {
520         const char *arg = argv[i];
521
522         if (arg[0] != '-') {
523             break;
524         }
525
526         if (!strcmp(arg, "--")) {
527             break;
528         } else if (!strcmp(arg, "-b")) {
529             retrace::debug = false;
530             retrace::verbosity = -1;
531         } else if (!strcmp(arg, "-c")) {
532             comparePrefix = argv[++i];
533             if (compareFrequency.empty()) {
534                 compareFrequency = trace::CallSet(trace::FREQUENCY_FRAME);
535             }
536         } else if (!strcmp(arg, "-C")) {
537             compareFrequency = trace::CallSet(argv[++i]);
538             if (comparePrefix == NULL) {
539                 comparePrefix = "";
540             }
541         } else if (!strcmp(arg, "-D")) {
542             dumpStateCallNo = atoi(argv[++i]);
543             dumpingState = true;
544             retrace::verbosity = -2;
545         } else if (!strcmp(arg, "-core")) {
546             retrace::coreProfile = true;
547         } else if (!strcmp(arg, "-db")) {
548             retrace::doubleBuffer = true;
549         } else if (!strcmp(arg, "-sb")) {
550             retrace::doubleBuffer = false;
551         } else if (!strcmp(arg, "--help")) {
552             usage(argv[0]);
553             return 0;
554         } else if (!strcmp(arg, "-s")) {
555             snapshotPrefix = argv[++i];
556             if (snapshotFrequency.empty()) {
557                 snapshotFrequency = trace::CallSet(trace::FREQUENCY_FRAME);
558             }
559             if (snapshotPrefix[0] == '-' && snapshotPrefix[1] == 0) {
560                 os::setBinaryMode(stdout);
561                 retrace::verbosity = -2;
562             }
563         } else if (!strcmp(arg, "-S")) {
564             snapshotFrequency = trace::CallSet(argv[++i]);
565             if (snapshotPrefix == NULL) {
566                 snapshotPrefix = "";
567             }
568         } else if (!strcmp(arg, "-v")) {
569             ++retrace::verbosity;
570         } else if (!strcmp(arg, "-w")) {
571             waitOnFinish = true;
572         } else if (arg[1] == 'p') {
573             retrace::debug = false;
574             retrace::profiling = true;
575             retrace::verbosity = -1;
576
577             if (!strcmp(arg, "-pcpu")) {
578                 retrace::profilingCpuTimes = true;
579             } else if (!strcmp(arg, "-pgpu")) {
580                 retrace::profilingGpuTimes = true;
581             } else if (!strcmp(arg, "-ppd")) {
582                 retrace::profilingPixelsDrawn = true;
583             }
584         } else {
585             std::cerr << "error: unknown option " << arg << "\n";
586             usage(argv[0]);
587             return 1;
588         }
589     }
590
591     retrace::setUp();
592     if (retrace::profiling) {
593         retrace::profiler.setup(retrace::profilingCpuTimes, retrace::profilingGpuTimes, retrace::profilingPixelsDrawn);
594     }
595
596     for ( ; i < argc; ++i) {
597         if (!retrace::parser.open(argv[i])) {
598             std::cerr << "error: failed to open " << argv[i] << "\n";
599             return 1;
600         }
601
602         retrace::mainLoop();
603
604         retrace::parser.close();
605     }
606
607     // XXX: X often hangs on XCloseDisplay
608     //retrace::cleanUp();
609
610     return 0;
611 }
612