#include "os_binary.hpp"
#include "os_time.hpp"
+#include "os_thread.hpp"
#include "image.hpp"
#include "trace_callset.hpp"
#include "trace_dump.hpp"
static unsigned dumpStateCallNo = ~0;
+retrace::Retracer retracer;
+
namespace retrace {
trace::Parser parser;
+trace::Profiler profiler;
int verbosity = 0;
bool debug = true;
-bool profiling = false;
-
+bool dumpingState = false;
bool doubleBuffer = true;
bool coreProfile = false;
+bool profiling = false;
+bool profilingGpuTimes = false;
+bool profilingCpuTimes = false;
+bool profilingPixelsDrawn = false;
-static unsigned frameNo = 0;
+unsigned frameNo = 0;
+unsigned callNo = 0;
void
}
+class RelayRunner;
+
+
static void
-mainLoop() {
- retrace::Retracer retracer;
+retraceCall(trace::Call *call) {
+ bool swapRenderTarget = call->flags &
+ trace::CALL_FLAG_SWAP_RENDERTARGET;
+ bool doSnapshot = snapshotFrequency.contains(*call) ||
+ compareFrequency.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);
+ }
+ }
- addCallbacks(retracer);
+ callNo = call->no;
+ retracer.retrace(*call);
- long long startTime = 0;
- frameNo = 0;
+ if (doSnapshot && !swapRenderTarget)
+ takeSnapshot(call->no);
- startTime = os::getTime();
- trace::Call *call;
+ if (call->no >= dumpStateCallNo &&
+ dumpState(std::cout)) {
+ exit(0);
+ }
+}
- while ((call = retrace::parser.parse_call())) {
- bool swapRenderTarget = call->flags & trace::CALL_FLAG_SWAP_RENDERTARGET;
- bool doSnapshot =
- snapshotFrequency.contains(*call) ||
- compareFrequency.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);
+
+class RelayRace
+{
+public:
+ std::vector<RelayRunner*> runners;
+
+ RelayRace();
+
+ RelayRunner *
+ getRunner(unsigned leg);
+
+ void
+ startRace(void);
+
+ void
+ passBaton(trace::Call *call);
+
+ void
+ finishRace();
+};
+
+
+class RelayRunner
+{
+public:
+ RelayRace *race;
+ unsigned leg;
+ os::mutex mutex;
+ os::condition_variable wake_cond;
+
+ bool finished;
+ trace::Call *baton;
+ os::thread *thread;
+
+ static void *
+ runnerThread(RelayRunner *_this);
+
+ RelayRunner(RelayRace *race, unsigned _leg) :
+ race(race),
+ leg(_leg),
+ finished(false),
+ baton(0),
+ thread(0)
+ {
+ if (leg) {
+ thread = new os::thread(runnerThread, this);
+ }
+ }
+
+ void
+ runRace(void) {
+ os::unique_lock<os::mutex> lock(mutex);
+
+ while (1) {
+ while (!finished && !baton) {
+ wake_cond.wait(lock);
+ }
+
+ if (finished) {
+ break;
}
+
+ assert(baton);
+ trace::Call *call = baton;
+ baton = 0;
+
+ runLeg(call);
}
- retracer.retrace(*call);
+ if (0) std::cerr << "leg " << leg << " actually finishing\n";
- if (doSnapshot && !swapRenderTarget) {
- takeSnapshot(call->no);
+ if (leg == 0) {
+ std::vector<RelayRunner*>::iterator it;
+ for (it = race->runners.begin() + 1; it != race->runners.end(); ++it) {
+ RelayRunner* runner = *it;
+ runner->finishRace();
+ }
}
+ }
- if (call->no >= dumpStateCallNo &&
- dumpState(std::cout)) {
- exit(0);
+ void runLeg(trace::Call *call) {
+ do {
+ assert(call);
+ assert(call->thread_id == leg);
+ retraceCall(call);
+ delete call;
+ call = parser.parse_call();
+ } while (call && call->thread_id == leg);
+
+ if (call) {
+ assert(call->thread_id != leg);
+ flushRendering();
+ race->passBaton(call);
+ } else {
+ if (0) std::cerr << "finished on leg " << leg << "\n";
+ if (leg) {
+ race->finishRace();
+ } else {
+ finished = true;
+ }
}
+ }
+
+ void receiveBaton(trace::Call *call) {
+ assert (call->thread_id == leg);
+
+ mutex.lock();
+ baton = call;
+ mutex.unlock();
+
+ wake_cond.signal();
+ }
+
+ void finishRace() {
+ if (0) std::cerr << "notify finish to leg " << leg << "\n";
+
+ mutex.lock();
+ finished = true;
+ mutex.unlock();
- delete call;
+ wake_cond.signal();
}
+};
- // Reached the end of trace
- flushRendering();
+void *
+RelayRunner::runnerThread(RelayRunner *_this) {
+ _this->runRace();
+ return 0;
+}
+
+
+RelayRace::RelayRace() {
+ runners.push_back(new RelayRunner(this, 0));
+}
+
+RelayRunner *
+RelayRace::getRunner(unsigned leg) {
+ RelayRunner *runner;
+
+ if (leg >= runners.size()) {
+ runners.resize(leg + 1);
+ runner = 0;
+ } else {
+ runner = runners[leg];
+ }
+ if (!runner) {
+ runner = new RelayRunner(this, leg);
+ runners[leg] = runner;
+ }
+ return runner;
+}
+
+void
+RelayRace::startRace(void) {
+ trace::Call *call;
+ call = parser.parse_call();
+
+ if (!call) {
+ return;
+ }
+
+ assert(call->thread_id == 0);
+
+ RelayRunner *foreRunner = getRunner(0);
+ if (call->thread_id == 0) {
+ foreRunner->baton = call;
+ } else {
+ passBaton(call);
+ }
+
+ foreRunner->runRace();
+}
+
+void
+RelayRace::passBaton(trace::Call *call) {
+ if (0) std::cerr << "switching to thread " << call->thread_id << "\n";
+ RelayRunner *runner = getRunner(call->thread_id);
+ runner->receiveBaton(call);
+}
+
+void
+RelayRace::finishRace(void) {
+ RelayRunner *runner = getRunner(0);
+ runner->finishRace();
+}
+
+
+static void
+mainLoop() {
+ addCallbacks(retracer);
+
+ long long startTime = 0;
+ frameNo = 0;
+
+ startTime = os::getTime();
+
+ RelayRace race;
+ race.startRace();
long long endTime = os::getTime();
float timeInterval = (endTime - startTime) * (1.0 / os::timeFrequency);
"Replay TRACE.\n"
"\n"
" -b benchmark mode (no error checking or warning messages)\n"
- " -p profiling mode (run whole trace, dump profiling info)\n"
+ " -pcpu cpu profiling (cpu times per call)\n"
+ " -pgpu gpu profiling (gpu times per draw call)\n"
+ " -ppd pixels drawn profiling (pixels drawn per draw call)\n"
" -c PREFIX compare against snapshots\n"
" -C CALLSET calls to compare (default is every frame)\n"
" -core use core profile\n"
extern "C"
int main(int argc, char **argv)
{
+ using namespace retrace;
+
assert(compareFrequency.empty());
assert(snapshotFrequency.empty());
} else if (!strcmp(arg, "-b")) {
retrace::debug = false;
retrace::verbosity = -1;
- } else if (!strcmp(arg, "-p")) {
- retrace::debug = false;
- retrace::profiling = true;
- retrace::verbosity = -1;
} else if (!strcmp(arg, "-c")) {
comparePrefix = argv[++i];
if (compareFrequency.empty()) {
}
} else if (!strcmp(arg, "-D")) {
dumpStateCallNo = atoi(argv[++i]);
+ dumpingState = true;
retrace::verbosity = -2;
} else if (!strcmp(arg, "-core")) {
retrace::coreProfile = true;
++retrace::verbosity;
} else if (!strcmp(arg, "-w")) {
waitOnFinish = true;
+ } else if (arg[1] == 'p') {
+ retrace::debug = false;
+ retrace::profiling = true;
+ retrace::verbosity = -1;
+
+ if (!strcmp(arg, "-pcpu")) {
+ retrace::profilingCpuTimes = true;
+ } else if (!strcmp(arg, "-pgpu")) {
+ retrace::profilingGpuTimes = true;
+ } else if (!strcmp(arg, "-ppd")) {
+ retrace::profilingPixelsDrawn = true;
+ }
} else {
std::cerr << "error: unknown option " << arg << "\n";
usage(argv[0]);
}
retrace::setUp();
+ if (retrace::profiling) {
+ retrace::profiler.setup(retrace::profilingCpuTimes, retrace::profilingGpuTimes, retrace::profilingPixelsDrawn);
+ }
for ( ; i < argc; ++i) {
if (!retrace::parser.open(argv[i])) {
retrace::parser.close();
}
- retrace::cleanUp();
+ // XXX: X often hangs on XCloseDisplay
+ //retrace::cleanUp();
return 0;
}