1 /**************************************************************************
3 * Copyright 2011 Jose Fonseca
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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
24 **************************************************************************/
30 #include "os_binary.hpp"
31 #include "os_time.hpp"
32 #include "os_thread.hpp"
34 #include "trace_callset.hpp"
35 #include "trace_dump.hpp"
36 #include "retrace.hpp"
39 static bool waitOnFinish = false;
41 static const char *comparePrefix = NULL;
42 static const char *snapshotPrefix = NULL;
43 static trace::CallSet snapshotFrequency;
44 static trace::CallSet compareFrequency;
46 static unsigned dumpStateCallNo = ~0;
48 retrace::Retracer retracer;
55 trace::Profiler profiler;
60 bool dumpingState = false;
62 bool doubleBuffer = true;
63 bool coreProfile = false;
65 bool profiling = false;
66 bool profilingGpuTimes = false;
67 bool profilingCpuTimes = false;
68 bool profilingPixelsDrawn = false;
75 frameComplete(trace::Call &call) {
81 takeSnapshot(unsigned call_no) {
82 assert(snapshotPrefix || comparePrefix);
84 image::Image *ref = NULL;
87 os::String filename = os::String::format("%s%010u.png", comparePrefix, call_no);
88 ref = image::readPNG(filename);
92 if (retrace::verbosity >= 0) {
93 std::cout << "Read " << filename << "\n";
97 image::Image *src = getSnapshot();
102 if (snapshotPrefix) {
103 if (snapshotPrefix[0] == '-' && snapshotPrefix[1] == 0) {
105 snprintf(comment, sizeof comment, "%u", call_no);
106 src->writePNM(std::cout, comment);
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";
116 std::cout << "Snapshot " << call_no << " average precision of " << src->compare(*ref) << " bits\n";
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);
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);
144 // Whereas for ordinate fbo/rendertarget changes we
145 // use the previous call's number.
146 takeSnapshot(call->no - 1);
151 retracer.retrace(*call);
153 if (doSnapshot && !swapRenderTarget)
154 takeSnapshot(call->no);
156 if (call->no >= dumpStateCallNo &&
157 dumpState(std::cout)) {
166 std::vector<RelayRunner*> runners;
171 getRunner(unsigned leg);
177 passBaton(trace::Call *call);
190 os::condition_variable wake_cond;
197 runnerThread(RelayRunner *_this);
199 RelayRunner(RelayRace *race, unsigned _leg) :
207 thread = new os::thread(runnerThread, this);
213 os::unique_lock<os::mutex> lock(mutex);
216 while (!finished && !baton) {
217 wake_cond.wait(lock);
225 trace::Call *call = baton;
231 if (0) std::cerr << "leg " << leg << " actually finishing\n";
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();
242 void runLeg(trace::Call *call) {
245 assert(call->thread_id == leg);
248 call = parser.parse_call();
249 } while (call && call->thread_id == leg);
252 assert(call->thread_id != leg);
254 race->passBaton(call);
256 if (0) std::cerr << "finished on leg " << leg << "\n";
265 void receiveBaton(trace::Call *call) {
266 assert (call->thread_id == leg);
276 if (0) std::cerr << "notify finish to leg " << leg << "\n";
287 RelayRunner::runnerThread(RelayRunner *_this) {
293 RelayRace::RelayRace() {
294 runners.push_back(new RelayRunner(this, 0));
298 RelayRace::getRunner(unsigned leg) {
301 if (leg >= runners.size()) {
302 runners.resize(leg + 1);
305 runner = runners[leg];
308 runner = new RelayRunner(this, leg);
309 runners[leg] = runner;
315 RelayRace::startRace(void) {
317 call = parser.parse_call();
323 assert(call->thread_id == 0);
325 RelayRunner *foreRunner = getRunner(0);
326 if (call->thread_id == 0) {
327 foreRunner->baton = call;
332 foreRunner->runRace();
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);
343 RelayRace::finishRace(void) {
344 RelayRunner *runner = getRunner(0);
345 runner->finishRace();
351 addCallbacks(retracer);
353 long long startTime = 0;
356 startTime = os::getTime();
361 long long endTime = os::getTime();
362 float timeInterval = (endTime - startTime) * (1.0 / os::timeFrequency);
364 if ((retrace::verbosity >= -1) || (retrace::profiling)) {
366 "Rendered " << frameNo << " frames"
367 " in " << timeInterval << " secs,"
368 " average of " << (frameNo/timeInterval) << " fps\n";
379 } /* namespace retrace */
383 usage(const char *argv0) {
385 "Usage: " << argv0 << " [OPTION] TRACE [...]\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";
406 int main(int argc, char **argv)
408 using namespace retrace;
410 assert(compareFrequency.empty());
411 assert(snapshotFrequency.empty());
414 for (i = 1; i < argc; ++i) {
415 const char *arg = argv[i];
421 if (!strcmp(arg, "--")) {
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);
431 } else if (!strcmp(arg, "-C")) {
432 compareFrequency = trace::CallSet(argv[++i]);
433 if (comparePrefix == NULL) {
436 } else if (!strcmp(arg, "-D")) {
437 dumpStateCallNo = atoi(argv[++i]);
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")) {
449 } else if (!strcmp(arg, "-s")) {
450 snapshotPrefix = argv[++i];
451 if (snapshotFrequency.empty()) {
452 snapshotFrequency = trace::CallSet(trace::FREQUENCY_FRAME);
454 if (snapshotPrefix[0] == '-' && snapshotPrefix[1] == 0) {
455 os::setBinaryMode(stdout);
456 retrace::verbosity = -2;
458 } else if (!strcmp(arg, "-S")) {
459 snapshotFrequency = trace::CallSet(argv[++i]);
460 if (snapshotPrefix == NULL) {
463 } else if (!strcmp(arg, "-v")) {
464 ++retrace::verbosity;
465 } else if (!strcmp(arg, "-w")) {
467 } else if (arg[1] == 'p') {
468 retrace::debug = false;
469 retrace::profiling = true;
470 retrace::verbosity = -1;
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;
480 std::cerr << "error: unknown option " << arg << "\n";
487 if (retrace::profiling) {
488 retrace::profiler.setup(retrace::profilingCpuTimes, retrace::profilingGpuTimes, retrace::profilingPixelsDrawn);
491 for ( ; i < argc; ++i) {
492 if (!retrace::parser.open(argv[i])) {
493 std::cerr << "error: failed to open " << argv[i] << "\n";
499 retrace::parser.close();
502 // XXX: X often hangs on XCloseDisplay
503 //retrace::cleanUp();