X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=retrace%2Fretrace_main.cpp;h=7e171e67bf97f504744ab2268f94d84362a6f7a7;hb=b032a6f55c364f23206cd619d33bb1cf9867657d;hp=9b029c8b5a10e4c0d6c5c9794257b1f74d6b281a;hpb=32b223c5dca493e873d0a58a859ca4ff3e8fbd9c;p=apitrace diff --git a/retrace/retrace_main.cpp b/retrace/retrace_main.cpp index 9b029c8..7e171e6 100644 --- a/retrace/retrace_main.cpp +++ b/retrace/retrace_main.cpp @@ -29,7 +29,7 @@ #include "os_binary.hpp" #include "os_time.hpp" -#include "os_workqueue.hpp" +#include "os_thread.hpp" #include "image.hpp" #include "trace_callset.hpp" #include "trace_dump.hpp" @@ -37,7 +37,6 @@ static bool waitOnFinish = false; -static bool use_threads; static const char *comparePrefix = NULL; static const char *snapshotPrefix = NULL; @@ -55,7 +54,6 @@ namespace retrace { trace::Parser parser; trace::Profiler profiler; -static std::map thread_wq_map; int verbosity = 0; bool debug = true; @@ -71,22 +69,7 @@ bool profilingPixelsDrawn = false; unsigned frameNo = 0; unsigned callNo = 0; -static bool state_dumped; -class RenderWork : public os::WorkQueueWork -{ - trace::Call *call; -public: - void run(void); - RenderWork(trace::Call *_call) { call = _call; } - ~RenderWork(void) { delete call; } -}; - -class FlushGLWork : public os::WorkQueueWork -{ -public: - void run(void) { flushRendering(); } -}; void frameComplete(trace::Call &call) { @@ -94,6 +77,9 @@ frameComplete(trace::Call &call) { } +/** + * Take/compare snapshots. + */ static void takeSnapshot(unsigned call_no) { assert(snapshotPrefix || comparePrefix); @@ -139,16 +125,20 @@ takeSnapshot(unsigned call_no) { return; } -void RenderWork::run(void) -{ + +/** + * 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) || compareFrequency.contains(*call); - if (state_dumped) - return; - // For calls which cause rendertargets to be swaped, we take the // snapshot _before_ swapping the rendertargets. if (doSnapshot && swapRenderTarget) { @@ -169,73 +159,295 @@ void RenderWork::run(void) if (doSnapshot && !swapRenderTarget) takeSnapshot(call->no); - if (call->no >= dumpStateCallNo && dumpState(std::cout)) - state_dumped = true; + if (call->no >= dumpStateCallNo && + dumpState(std::cout)) { + exit(0); + } } -static os::WorkQueue *get_work_queue(unsigned long thread_id) + +class RelayRunner; + + +/** + * Implement multi-threading by mimicking a relay race. + */ +class RelayRace { - os::WorkQueue *thread; - std::map::iterator it; +private: + /** + * Runners indexed by the leg they run (i.e, the thread_ids from the + * trace). + */ + std::vector runners; - it = thread_wq_map.find(thread_id); - if (it == thread_wq_map.end()) { - thread = new os::WorkQueue(); - thread_wq_map[thread_id] = thread; - } else { - thread = it->second; +public: + RelayRace(); + + ~RelayRace(); + + RelayRunner * + getRunner(unsigned leg); + + inline RelayRunner * + getForeRunner() { + return getRunner(0); } - return thread; -} + void + run(void); -static void exit_work_queues(void) + void + passBaton(trace::Call *call); + + void + finishLine(); + + void + stopRunners(); +}; + + +/** + * Each runner is a thread. + * + * The fore runner doesn't have its own thread, but instead uses the thread + * where the race started. + */ +class RelayRunner { - std::map::iterator it; +private: + friend class RelayRace; + + RelayRace *race; + + unsigned leg; + + os::mutex mutex; + os::condition_variable wake_cond; + + /** + * There are protected by the mutex. + */ + bool finished; + trace::Call *baton; + + os::thread thread; - it = thread_wq_map.begin(); - while (it != thread_wq_map.end()) { - os::WorkQueue *thread_wq = it->second; + static void * + runnerThread(RelayRunner *_this); - thread_wq->queue_work(new FlushGLWork); - thread_wq->flush(); - thread_wq->destroy(); - thread_wq_map.erase(it++); +public: + RelayRunner(RelayRace *race, unsigned _leg) : + race(race), + leg(_leg), + finished(false), + baton(0) + { + /* The fore runner does not need a new thread */ + if (leg) { + thread = os::thread(runnerThread, this); + } } -} -static void do_all_calls(void) -{ - trace::Call *call; - int prev_thread_id = -1; - os::WorkQueue *thread_wq = NULL; - - while ((call = parser.parse_call())) { - RenderWork *render_work = new RenderWork(call); - - if (use_threads) { - if (prev_thread_id != call->thread_id) { - if (thread_wq) - thread_wq->flush(); - thread_wq = get_work_queue(call->thread_id); - prev_thread_id = call->thread_id; + /** + * Thread main loop. + */ + void + runRace(void) { + os::unique_lock lock(mutex); + + while (1) { + while (!finished && !baton) { + wake_cond.wait(lock); } - thread_wq->queue_work(render_work); + if (finished) { + break; + } + + assert(baton); + trace::Call *call = baton; + baton = 0; + + runLeg(call); + } + + if (0) std::cerr << "leg " << leg << " actually finishing\n"; + + if (leg == 0) { + race->stopRunners(); + } + } - // XXX: Flush immediately to avoid race conditions on unprotected - // static/global variables. - thread_wq->flush(); + /** + * Interpret successive calls. + */ + void + runLeg(trace::Call *call) { + /* Consume successive calls for this thread. */ + do { + assert(call); + assert(call->thread_id == leg); + retraceCall(call); + delete call; + call = parser.parse_call(); + } while (call && call->thread_id == leg); + + if (call) { + /* Pass the baton */ + assert(call->thread_id != leg); + flushRendering(); + race->passBaton(call); } else { - render_work->run(); - delete render_work; + /* Reached the finish line */ + if (0) std::cerr << "finished on leg " << leg << "\n"; + if (leg) { + /* Notify the fore runner */ + race->finishLine(); + } else { + /* We are the fore runner */ + finished = true; + } } + } - if (state_dumped) - break; + /** + * Called by other threads when relinquishing the baton. + */ + void + receiveBaton(trace::Call *call) { + assert (call->thread_id == leg); + + mutex.lock(); + baton = call; + mutex.unlock(); + + wake_cond.signal(); + } + + /** + * Called by the fore runner when the race is over. + */ + void + finishRace() { + if (0) std::cerr << "notify finish to leg " << leg << "\n"; + + mutex.lock(); + finished = true; + mutex.unlock(); + + wake_cond.signal(); + } +}; + + +void * +RelayRunner::runnerThread(RelayRunner *_this) { + _this->runRace(); + return 0; +} + + +RelayRace::RelayRace() { + runners.push_back(new RelayRunner(this, 0)); +} + + +RelayRace::~RelayRace() { + assert(runners.size() >= 1); + std::vector::const_iterator it; + for (it = runners.begin(); it != runners.end(); ++it) { + RelayRunner* runner = *it; + if (runner) { + delete runner; + } + } +} + + +/** + * Get (or instantiate) a runner for the specified leg. + */ +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; +} + + +/** + * Start the race. + */ +void +RelayRace::run(void) { + trace::Call *call; + call = parser.parse_call(); + if (!call) { + /* Nothing to do */ + return; } - exit_work_queues(); + RelayRunner *foreRunner = getForeRunner(); + if (call->thread_id == 0) { + /* We are the forerunner thread, so no need to pass baton */ + foreRunner->baton = call; + } else { + passBaton(call); + } + + /* Start the forerunner thread */ + foreRunner->runRace(); +} + + +/** + * Pass the baton (i.e., the call) to the appropriate thread. + */ +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); +} + + +/** + * Called when a runner other than the forerunner reaches the finish line. + * + * Only the fore runner can finish the race, so inform him that the race is + * finished. + */ +void +RelayRace::finishLine(void) { + RelayRunner *foreRunner = getForeRunner(); + foreRunner->finishRace(); +} + + +/** + * Called by the fore runner after finish line to stop all other runners. + */ +void +RelayRace::stopRunners(void) { + std::vector::const_iterator it; + for (it = runners.begin() + 1; it != runners.end(); ++it) { + RelayRunner* runner = *it; + if (runner) { + runner->finishRace(); + } + } } @@ -248,14 +460,8 @@ mainLoop() { startTime = os::getTime(); - do_all_calls(); - - if (!use_threads) - /* - * Reached the end of trace; if using threads we do the flush - * when exiting the threads. - */ - flushRendering(); + RelayRace race; + race.run(); long long endTime = os::getTime(); float timeInterval = (endTime - startTime) * (1.0 / os::timeFrequency); @@ -297,8 +503,7 @@ usage(const char *argv0) { " -S CALLSET calls to snapshot (default is every frame)\n" " -v increase output verbosity\n" " -D CALLNO dump state at specific call no\n" - " -w waitOnFinish on final frame\n" - " -t enable threading\n"; + " -w waitOnFinish on final frame\n"; } @@ -376,8 +581,6 @@ int main(int argc, char **argv) } else if (!strcmp(arg, "-ppd")) { retrace::profilingPixelsDrawn = true; } - } else if (!strcmp(arg, "-t")) { - use_threads = true; } else { std::cerr << "error: unknown option " << arg << "\n"; usage(argv[0]);