]> git.cworth.org Git - apitrace/blob - retrace/retrace_main.cpp
f9eabcb3b5cfe26729f17e28ab3fbb39bad02ab7
[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 static void
81 takeSnapshot(unsigned call_no) {
82     assert(snapshotPrefix || comparePrefix);
83
84     image::Image *ref = NULL;
85
86     if (comparePrefix) {
87         os::String filename = os::String::format("%s%010u.png", comparePrefix, call_no);
88         ref = image::readPNG(filename);
89         if (!ref) {
90             return;
91         }
92         if (retrace::verbosity >= 0) {
93             std::cout << "Read " << filename << "\n";
94         }
95     }
96
97     image::Image *src = getSnapshot();
98     if (!src) {
99         return;
100     }
101
102     if (snapshotPrefix) {
103         if (snapshotPrefix[0] == '-' && snapshotPrefix[1] == 0) {
104             char comment[21];
105             snprintf(comment, sizeof comment, "%u", call_no);
106             src->writePNM(std::cout, comment);
107         } else {
108             os::String filename = os::String::format("%s%010u.png", snapshotPrefix, call_no);
109             if (src->writePNG(filename) && retrace::verbosity >= 0) {
110                 std::cout << "Wrote " << filename << "\n";
111             }
112         }
113     }
114
115     if (ref) {
116         std::cout << "Snapshot " << call_no << " average precision of " << src->compare(*ref) << " bits\n";
117         delete ref;
118     }
119
120     delete src;
121
122     return;
123 }
124
125
126 class RelayRunner;
127
128
129 static void
130 retraceCall(trace::Call *call) {
131     bool swapRenderTarget = call->flags &
132         trace::CALL_FLAG_SWAP_RENDERTARGET;
133     bool doSnapshot = snapshotFrequency.contains(*call) ||
134         compareFrequency.contains(*call);
135
136     // For calls which cause rendertargets to be swaped, we take the
137     // snapshot _before_ swapping the rendertargets.
138     if (doSnapshot && swapRenderTarget) {
139         if (call->flags & trace::CALL_FLAG_END_FRAME) {
140             // For swapbuffers/presents we still use this
141             // call number, spite not have been executed yet.
142             takeSnapshot(call->no);
143         } else {
144             // Whereas for ordinate fbo/rendertarget changes we
145             // use the previous call's number.
146             takeSnapshot(call->no - 1);
147         }
148     }
149
150     callNo = call->no;
151     retracer.retrace(*call);
152
153     if (doSnapshot && !swapRenderTarget)
154         takeSnapshot(call->no);
155
156     if (call->no >= dumpStateCallNo &&
157         dumpState(std::cout)) {
158         exit(0);
159     }
160 }
161
162
163 class RelayRace
164 {
165 public:
166     std::vector<RelayRunner*> runners;
167
168     RelayRace();
169
170     RelayRunner *
171     getRunner(unsigned leg);
172
173     void
174     startRace(void);
175
176     void
177     passBaton(trace::Call *call);
178
179     void
180     finishRace();
181 };
182
183
184 class RelayRunner
185 {
186 public:
187     RelayRace *race;
188     unsigned leg;
189     os::mutex mutex;
190     os::condition_variable wake_cond;
191
192     bool finished;
193     trace::Call *baton;
194     os::thread *thread;
195
196     static void *
197     runnerThread(RelayRunner *_this);
198
199     RelayRunner(RelayRace *race, unsigned _leg) :
200         race(race),
201         leg(_leg),
202         finished(false),
203         baton(0),
204         thread(0)
205     {
206         if (leg) {
207             thread = new os::thread(runnerThread, this);
208         }
209     }
210
211     void
212     runRace(void) {
213         os::unique_lock<os::mutex> lock(mutex);
214
215         while (1) {
216             while (!finished && !baton) {
217                 wake_cond.wait(lock);
218             }
219
220             if (finished) {
221                 break;
222             }
223
224             assert(baton);
225             trace::Call *call = baton;
226             baton = 0;
227
228             runLeg(call);
229         }
230
231         if (0) std::cerr << "leg " << leg << " actually finishing\n";
232
233         if (leg == 0) {
234             std::vector<RelayRunner*>::iterator it;
235             for (it = race->runners.begin() + 1; it != race->runners.end(); ++it) {
236                 RelayRunner* runner = *it;
237                 runner->finishRace();
238             }
239         }
240     }
241
242     void runLeg(trace::Call *call) {
243         do {
244             assert(call);
245             assert(call->thread_id == leg);
246             retraceCall(call);
247             delete call;
248             call = parser.parse_call();
249         } while (call && call->thread_id == leg);
250
251         if (call) {
252             assert(call->thread_id != leg);
253             flushRendering();
254             race->passBaton(call);
255         } else {
256             if (0) std::cerr << "finished on leg " << leg << "\n";
257             if (leg) {
258                 race->finishRace();
259             } else {
260                 finished = true;
261             }
262         }
263     }
264
265     void receiveBaton(trace::Call *call) {
266         assert (call->thread_id == leg);
267
268         mutex.lock();
269         baton = call;
270         mutex.unlock();
271
272         wake_cond.signal();
273     }
274
275     void finishRace() {
276         if (0) std::cerr << "notify finish to leg " << leg << "\n";
277
278         mutex.lock();
279         finished = true;
280         mutex.unlock();
281
282         wake_cond.signal();
283     }
284 };
285
286 void *
287 RelayRunner::runnerThread(RelayRunner *_this) {
288     _this->runRace();
289     return 0;
290 }
291
292
293 RelayRace::RelayRace() {
294     runners.push_back(new RelayRunner(this, 0));
295 }
296
297 RelayRunner *
298 RelayRace::getRunner(unsigned leg) {
299     RelayRunner *runner;
300
301     if (leg >= runners.size()) {
302         runners.resize(leg + 1);
303         runner = 0;
304     } else {
305         runner = runners[leg];
306     }
307     if (!runner) {
308         runner = new RelayRunner(this, leg);
309         runners[leg] = runner;
310     }
311     return runner;
312 }
313
314 void
315 RelayRace::startRace(void) {
316     trace::Call *call;
317     call = parser.parse_call();
318
319     if (!call) {
320         return;
321     }
322
323     assert(call->thread_id == 0);
324
325     RelayRunner *foreRunner = getRunner(0);
326     if (call->thread_id == 0) {
327         foreRunner->baton = call;
328     } else {
329         passBaton(call);
330     }
331
332     foreRunner->runRace();
333 }
334
335 void
336 RelayRace::passBaton(trace::Call *call) {
337     if (0) std::cerr << "switching to thread " << call->thread_id << "\n";
338     RelayRunner *runner = getRunner(call->thread_id);
339     runner->receiveBaton(call);
340 }
341
342 void
343 RelayRace::finishRace(void) {
344     RelayRunner *runner = getRunner(0);
345     runner->finishRace();
346 }
347
348
349 static void
350 mainLoop() {
351     addCallbacks(retracer);
352
353     long long startTime = 0; 
354     frameNo = 0;
355
356     startTime = os::getTime();
357
358     RelayRace race;
359     race.startRace();
360
361     long long endTime = os::getTime();
362     float timeInterval = (endTime - startTime) * (1.0 / os::timeFrequency);
363
364     if ((retrace::verbosity >= -1) || (retrace::profiling)) {
365         std::cout << 
366             "Rendered " << frameNo << " frames"
367             " in " <<  timeInterval << " secs,"
368             " average of " << (frameNo/timeInterval) << " fps\n";
369     }
370
371     if (waitOnFinish) {
372         waitForInput();
373     } else {
374         return;
375     }
376 }
377
378
379 } /* namespace retrace */
380
381
382 static void
383 usage(const char *argv0) {
384     std::cout << 
385         "Usage: " << argv0 << " [OPTION] TRACE [...]\n"
386         "Replay TRACE.\n"
387         "\n"
388         "  -b           benchmark mode (no error checking or warning messages)\n"
389         "  -pcpu        cpu profiling (cpu times per call)\n"
390         "  -pgpu        gpu profiling (gpu times per draw call)\n"
391         "  -ppd         pixels drawn profiling (pixels drawn per draw call)\n"
392         "  -c PREFIX    compare against snapshots\n"
393         "  -C CALLSET   calls to compare (default is every frame)\n"
394         "  -core        use core profile\n"
395         "  -db          use a double buffer visual (default)\n"
396         "  -sb          use a single buffer visual\n"
397         "  -s PREFIX    take snapshots; `-` for PNM stdout output\n"
398         "  -S CALLSET   calls to snapshot (default is every frame)\n"
399         "  -v           increase output verbosity\n"
400         "  -D CALLNO    dump state at specific call no\n"
401         "  -w           waitOnFinish on final frame\n";
402 }
403
404
405 extern "C"
406 int main(int argc, char **argv)
407 {
408     using namespace retrace;
409
410     assert(compareFrequency.empty());
411     assert(snapshotFrequency.empty());
412
413     int i;
414     for (i = 1; i < argc; ++i) {
415         const char *arg = argv[i];
416
417         if (arg[0] != '-') {
418             break;
419         }
420
421         if (!strcmp(arg, "--")) {
422             break;
423         } else if (!strcmp(arg, "-b")) {
424             retrace::debug = false;
425             retrace::verbosity = -1;
426         } else if (!strcmp(arg, "-c")) {
427             comparePrefix = argv[++i];
428             if (compareFrequency.empty()) {
429                 compareFrequency = trace::CallSet(trace::FREQUENCY_FRAME);
430             }
431         } else if (!strcmp(arg, "-C")) {
432             compareFrequency = trace::CallSet(argv[++i]);
433             if (comparePrefix == NULL) {
434                 comparePrefix = "";
435             }
436         } else if (!strcmp(arg, "-D")) {
437             dumpStateCallNo = atoi(argv[++i]);
438             dumpingState = true;
439             retrace::verbosity = -2;
440         } else if (!strcmp(arg, "-core")) {
441             retrace::coreProfile = true;
442         } else if (!strcmp(arg, "-db")) {
443             retrace::doubleBuffer = true;
444         } else if (!strcmp(arg, "-sb")) {
445             retrace::doubleBuffer = false;
446         } else if (!strcmp(arg, "--help")) {
447             usage(argv[0]);
448             return 0;
449         } else if (!strcmp(arg, "-s")) {
450             snapshotPrefix = argv[++i];
451             if (snapshotFrequency.empty()) {
452                 snapshotFrequency = trace::CallSet(trace::FREQUENCY_FRAME);
453             }
454             if (snapshotPrefix[0] == '-' && snapshotPrefix[1] == 0) {
455                 os::setBinaryMode(stdout);
456                 retrace::verbosity = -2;
457             }
458         } else if (!strcmp(arg, "-S")) {
459             snapshotFrequency = trace::CallSet(argv[++i]);
460             if (snapshotPrefix == NULL) {
461                 snapshotPrefix = "";
462             }
463         } else if (!strcmp(arg, "-v")) {
464             ++retrace::verbosity;
465         } else if (!strcmp(arg, "-w")) {
466             waitOnFinish = true;
467         } else if (arg[1] == 'p') {
468             retrace::debug = false;
469             retrace::profiling = true;
470             retrace::verbosity = -1;
471
472             if (!strcmp(arg, "-pcpu")) {
473                 retrace::profilingCpuTimes = true;
474             } else if (!strcmp(arg, "-pgpu")) {
475                 retrace::profilingGpuTimes = true;
476             } else if (!strcmp(arg, "-ppd")) {
477                 retrace::profilingPixelsDrawn = true;
478             }
479         } else {
480             std::cerr << "error: unknown option " << arg << "\n";
481             usage(argv[0]);
482             return 1;
483         }
484     }
485
486     retrace::setUp();
487     if (retrace::profiling) {
488         retrace::profiler.setup(retrace::profilingCpuTimes, retrace::profilingGpuTimes, retrace::profilingPixelsDrawn);
489     }
490
491     for ( ; i < argc; ++i) {
492         if (!retrace::parser.open(argv[i])) {
493             std::cerr << "error: failed to open " << argv[i] << "\n";
494             return 1;
495         }
496
497         retrace::mainLoop();
498
499         retrace::parser.close();
500     }
501
502     // XXX: X often hangs on XCloseDisplay
503     //retrace::cleanUp();
504
505     return 0;
506 }
507