static void display(void) { startTime = OS::GetTime(); Trace::Call *call; while ((call = parser.parse_call())) { const char *name = call->name(); if (retrace::verbosity >= 1) { std::cout << *call; std::cout.flush(); } if (name[0] == 'C' && name[1] == 'G' && name[2] == 'L') { glretrace::retrace_call_cgl(*call); } else if (name[0] == 'w' && name[1] == 'g' && name[2] == 'l') { glretrace::retrace_call_wgl(*call); } else if (name[0] == 'g' && name[1] == 'l' && name[2] == 'X') { glretrace::retrace_call_glx(*call); } else { retrace::retrace_call(*call); } if (!insideGlBeginEnd && drawable && context && call->no >= dump_state) { glstate::dumpCurrentContext(std::cout); exit(0); } delete call; } // Reached the end of trace glFlush(); long long endTime = OS::GetTime(); float timeInterval = (endTime - startTime) * 1.0E-6; if (retrace::verbosity >= -1) { std::cout << "Rendered " << frame << " frames" " in " << timeInterval << " secs," " average of " << (frame/timeInterval) << " fps\n"; } if (wait) { while (ws->processEvents()) {} } else { exit(0); } }
namespace retrace { trace::Parser parser; trace::Profiler profiler; int verbosity = 0; unsigned debug = 1; bool dumpingState = false; bool dumpingSnapshots = false; Driver driver = DRIVER_DEFAULT; const char *driverModule = NULL; bool doubleBuffer = true; unsigned samples = 1; bool profiling = false; bool profilingGpuTimes = false; bool profilingCpuTimes = false; bool profilingPixelsDrawn = false; bool profilingMemoryUsage = false; bool useCallNos = true; bool singleThread = false; unsigned frameNo = 0; unsigned callNo = 0; static void takeSnapshot(unsigned call_no); void frameComplete(trace::Call &call) { ++frameNo; if (!(call.flags & trace::CALL_FLAG_END_FRAME) && snapshotFrequency.contains(call)) { // This call doesn't have the end of frame flag, so take any snapshot // now. takeSnapshot(call.no); } } class DefaultDumper: public Dumper { public: image::Image * getSnapshot(void) { return NULL; } bool canDump(void) { return false; } void dumpState(StateWriter &writer) { assert(0); } }; static DefaultDumper defaultDumper; Dumper *dumper = &defaultDumper; typedef StateWriter *(*StateWriterFactory)(std::ostream &); static StateWriterFactory stateWriterFactory = createJSONStateWriter; /** * Take snapshots. */ static void takeSnapshot(unsigned call_no) { static unsigned snapshot_no = 0; assert(dumpingSnapshots); assert(snapshotPrefix); image::Image *src = dumper->getSnapshot(); if (!src) { std::cerr << call_no << ": warning: failed to get snapshot\n"; return; } if ((snapshotInterval == 0 || (snapshot_no % snapshotInterval) == 0)) { if (snapshotPrefix[0] == '-' && snapshotPrefix[1] == 0) { char comment[21]; snprintf(comment, sizeof comment, "%u", useCallNos ? call_no : snapshot_no); switch (snapshotFormat) { case PNM_FMT: src->writePNM(std::cout, comment); break; case RAW_RGB: src->writeRAW(std::cout); break; case RAW_MD5: src->writeMD5(std::cout); break; default: assert(0); break; } } else { os::String filename = os::String::format("%s%010u.png", snapshotPrefix, useCallNos ? call_no : snapshot_no); // Alpha channel often has bogus data, so strip it when writing // PNG images to disk to simplify visualization. bool strip_alpha = true; if (src->writePNG(filename, strip_alpha) && retrace::verbosity >= 0) { std::cout << "Wrote " << filename << "\n"; } } } delete src; snapshot_no++; return; } /** * Retrace one call. * * Take snapshots before/after retracing (as appropriate) and dispatch it to * the respective handler. */ static void retraceCall(trace::Call *call) { callNo = call->no; bool swapRenderTarget = call->flags & trace::CALL_FLAG_SWAP_RENDERTARGET; bool doSnapshot = snapshotFrequency.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); } } retracer.retrace(*call); if (doSnapshot) { if (!swapRenderTarget) { takeSnapshot(call->no); } if (call->no >= snapshotFrequency.getLast()) { exit(0); } } if (call->no >= dumpStateCallNo && dumper->canDump()) { StateWriter *writer = stateWriterFactory(std::cout); dumper->dumpState(*writer); delete writer; exit(0); } } class RelayRunner; /** * Implement multi-threading by mimicking a relay race. */ class RelayRace { private: /** * Runners indexed by the leg they run (i.e, the thread_ids from the * trace). */ std::vector<RelayRunner*> runners; public: RelayRace(); ~RelayRace(); RelayRunner * getRunner(unsigned leg); inline RelayRunner * getForeRunner() { return getRunner(0); } void run(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 { 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; static void runnerThread(RelayRunner *_this); 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); } } ~RelayRunner() { if (thread.joinable()) { thread.join(); } } /** * Thread main loop. */ 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); } if (0) std::cerr << "leg " << leg << " actually finishing\n"; if (leg == 0) { race->stopRunners(); } } /** * Interpret successive calls. */ void runLeg(trace::Call *call) { /* Consume successive calls for this thread. */ do { bool callEndsFrame = false; static trace::ParseBookmark frameStart; assert(call); assert(call->thread_id == leg); if (loopCount && call->flags & trace::CALL_FLAG_END_FRAME) { callEndsFrame = true; parser.getBookmark(frameStart); } retraceCall(call); delete call; call = parser.parse_call(); /* Restart last frame if looping is requested. */ if (loopCount) { if (!call) { parser.setBookmark(lastFrameStart); call = parser.parse_call(); if (loopCount > 0) { --loopCount; } } else if (callEndsFrame) { lastFrameStart = frameStart; } } } while (call && call->thread_id == leg); if (call) { /* Pass the baton */ assert(call->thread_id != leg); flushRendering(); race->passBaton(call); } else { /* 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; } } } /** * 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.notify_one(); } /** * 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.notify_one(); } }; void RelayRunner::runnerThread(RelayRunner *_this) { _this->runRace(); } RelayRace::RelayRace() { runners.push_back(new RelayRunner(this, 0)); } RelayRace::~RelayRace() { assert(runners.size() >= 1); std::vector<RelayRunner*>::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; } /* If the user wants to loop we need to get a bookmark target. We * usually get this after replaying a call that ends a frame, but * for a trace that has only one frame we need to get it at the * beginning. */ if (loopCount) { parser.getBookmark(lastFrameStart); } 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<RelayRunner*>::const_iterator it; for (it = runners.begin() + 1; it != runners.end(); ++it) { RelayRunner* runner = *it; if (runner) { runner->finishRace(); } } } static void mainLoop() { addCallbacks(retracer); long long startTime = 0; frameNo = 0; startTime = os::getTime(); if (singleThread) { trace::Call *call; while ((call = parser.parse_call())) { retraceCall(call); delete call; }; } else { RelayRace race; race.run(); } finishRendering(); long long endTime = os::getTime(); float timeInterval = (endTime - startTime) * (1.0 / os::timeFrequency); if ((retrace::verbosity >= -1) || (retrace::profiling)) { std::cout << "Rendered " << frameNo << " frames" " in " << timeInterval << " secs," " average of " << (frameNo/timeInterval) << " fps\n"; } if (waitOnFinish) { waitForInput(); } else { return; } } } /* namespace retrace */
namespace glretrace { bool double_buffer = true; bool insideGlBeginEnd = false; Trace::Parser parser; glws::WindowSystem *ws = NULL; glws::Visual *visual = NULL; glws::Drawable *drawable = NULL; glws::Context *context = NULL; unsigned frame = 0; long long startTime = 0; bool wait = false; bool benchmark = false; const char *compare_prefix = NULL; const char *snapshot_prefix = NULL; enum frequency snapshot_frequency = FREQUENCY_NEVER; unsigned dump_state = ~0; void checkGlError(Trace::Call &call) { GLenum error = glGetError(); if (error == GL_NO_ERROR) { return; } if (retrace::verbosity == 0) { std::cout << call; std::cout.flush(); } std::cerr << call.no << ": "; std::cerr << "warning: glGetError("; std::cerr << call.name(); std::cerr << ") = "; switch (error) { case GL_INVALID_ENUM: std::cerr << "GL_INVALID_ENUM"; break; case GL_INVALID_VALUE: std::cerr << "GL_INVALID_VALUE"; break; case GL_INVALID_OPERATION: std::cerr << "GL_INVALID_OPERATION"; break; case GL_STACK_OVERFLOW: std::cerr << "GL_STACK_OVERFLOW"; break; case GL_STACK_UNDERFLOW: std::cerr << "GL_STACK_UNDERFLOW"; break; case GL_OUT_OF_MEMORY: std::cerr << "GL_OUT_OF_MEMORY"; break; case GL_INVALID_FRAMEBUFFER_OPERATION: std::cerr << "GL_INVALID_FRAMEBUFFER_OPERATION"; break; case GL_TABLE_TOO_LARGE: std::cerr << "GL_TABLE_TOO_LARGE"; break; default: std::cerr << error; break; } std::cerr << "\n"; } void snapshot(unsigned call_no) { if (!drawable || (!snapshot_prefix && !compare_prefix)) { return; } Image::Image *ref = NULL; if (compare_prefix) { char filename[PATH_MAX]; snprintf(filename, sizeof filename, "%s%010u.png", compare_prefix, call_no); ref = Image::readPNG(filename); if (!ref) { return; } if (retrace::verbosity >= 0) { std::cout << "Read " << filename << "\n"; } } Image::Image *src = glstate::getDrawBufferImage(GL_RGBA); if (!src) { return; } if (snapshot_prefix) { char filename[PATH_MAX]; snprintf(filename, sizeof filename, "%s%010u.png", snapshot_prefix, call_no); if (src->writePNG(filename) && retrace::verbosity >= 0) { std::cout << "Wrote " << filename << "\n"; } } if (ref) { std::cout << "Snapshot " << call_no << " average precision of " << src->compare(*ref) << " bits\n"; delete ref; } delete src; } void frame_complete(unsigned call_no) { ++frame; if (snapshot_frequency == FREQUENCY_FRAME || snapshot_frequency == FREQUENCY_FRAMEBUFFER) { snapshot(call_no); } } static void display(void) { startTime = OS::GetTime(); Trace::Call *call; while ((call = parser.parse_call())) { const char *name = call->name(); if (retrace::verbosity >= 1) { std::cout << *call; std::cout.flush(); } if (name[0] == 'C' && name[1] == 'G' && name[2] == 'L') { glretrace::retrace_call_cgl(*call); } else if (name[0] == 'w' && name[1] == 'g' && name[2] == 'l') { glretrace::retrace_call_wgl(*call); } else if (name[0] == 'g' && name[1] == 'l' && name[2] == 'X') { glretrace::retrace_call_glx(*call); } else { retrace::retrace_call(*call); } if (!insideGlBeginEnd && drawable && context && call->no >= dump_state) { glstate::dumpCurrentContext(std::cout); exit(0); } delete call; } // Reached the end of trace glFlush(); long long endTime = OS::GetTime(); float timeInterval = (endTime - startTime) * 1.0E-6; if (retrace::verbosity >= -1) { std::cout << "Rendered " << frame << " frames" " in " << timeInterval << " secs," " average of " << (frame/timeInterval) << " fps\n"; } if (wait) { while (ws->processEvents()) {} } else { exit(0); } } static void usage(void) { std::cout << "Usage: glretrace [OPTION] TRACE\n" "Replay TRACE.\n" "\n" " -b benchmark mode (no error checking or warning messages)\n" " -c PREFIX compare against snapshots\n" " -db use a double buffer visual (default)\n" " -sb use a single buffer visual\n" " -s PREFIX take snapshots\n" " -S FREQUENCY snapshot frequency: frame (default), framebuffer, or draw\n" " -v verbose output\n" " -D CALLNO dump state at specific call no\n" " -w wait on final frame\n"; } extern "C" int main(int argc, char **argv) { int i; for (i = 1; i < argc; ++i) { const char *arg = argv[i]; if (arg[0] != '-') { break; } if (!strcmp(arg, "--")) { break; } else if (!strcmp(arg, "-b")) { benchmark = true; retrace::verbosity = -1; } else if (!strcmp(arg, "-c")) { compare_prefix = argv[++i]; if (snapshot_frequency == FREQUENCY_NEVER) { snapshot_frequency = FREQUENCY_FRAME; } } else if (!strcmp(arg, "-D")) { dump_state = atoi(argv[++i]); retrace::verbosity = -2; } else if (!strcmp(arg, "-db")) { double_buffer = true; } else if (!strcmp(arg, "-sb")) { double_buffer = false; } else if (!strcmp(arg, "--help")) { usage(); return 0; } else if (!strcmp(arg, "-s")) { snapshot_prefix = argv[++i]; if (snapshot_frequency == FREQUENCY_NEVER) { snapshot_frequency = FREQUENCY_FRAME; } } else if (!strcmp(arg, "-S")) { arg = argv[++i]; if (!strcmp(arg, "frame")) { snapshot_frequency = FREQUENCY_FRAME; } else if (!strcmp(arg, "framebuffer")) { snapshot_frequency = FREQUENCY_FRAMEBUFFER; } else if (!strcmp(arg, "draw")) { snapshot_frequency = FREQUENCY_DRAW; } else { std::cerr << "error: unknown frequency " << arg << "\n"; usage(); return 1; } if (snapshot_prefix == NULL) { snapshot_prefix = ""; } } else if (!strcmp(arg, "-v")) { ++retrace::verbosity; } else if (!strcmp(arg, "-w")) { wait = true; } else { std::cerr << "error: unknown option " << arg << "\n"; usage(); return 1; } } ws = glws::createNativeWindowSystem(); visual = ws->createVisual(double_buffer); for ( ; i < argc; ++i) { if (!parser.open(argv[i])) { std::cerr << "error: failed to open " << argv[i] << "\n"; return 1; } display(); parser.close(); } return 0; } } /* namespace glretrace */
int main(int argc, char **argv) { int i; for (i = 1; i < argc; ++i) { const char *arg = argv[i]; if (arg[0] != '-') { break; } if (!strcmp(arg, "--")) { break; } else if (!strcmp(arg, "-b")) { benchmark = true; retrace::verbosity = -1; } else if (!strcmp(arg, "-c")) { compare_prefix = argv[++i]; if (snapshot_frequency == FREQUENCY_NEVER) { snapshot_frequency = FREQUENCY_FRAME; } } else if (!strcmp(arg, "-D")) { dump_state = atoi(argv[++i]); retrace::verbosity = -2; } else if (!strcmp(arg, "-db")) { double_buffer = true; } else if (!strcmp(arg, "-sb")) { double_buffer = false; } else if (!strcmp(arg, "--help")) { usage(); return 0; } else if (!strcmp(arg, "-s")) { snapshot_prefix = argv[++i]; if (snapshot_frequency == FREQUENCY_NEVER) { snapshot_frequency = FREQUENCY_FRAME; } } else if (!strcmp(arg, "-S")) { arg = argv[++i]; if (!strcmp(arg, "frame")) { snapshot_frequency = FREQUENCY_FRAME; } else if (!strcmp(arg, "framebuffer")) { snapshot_frequency = FREQUENCY_FRAMEBUFFER; } else if (!strcmp(arg, "draw")) { snapshot_frequency = FREQUENCY_DRAW; } else { std::cerr << "error: unknown frequency " << arg << "\n"; usage(); return 1; } if (snapshot_prefix == NULL) { snapshot_prefix = ""; } } else if (!strcmp(arg, "-v")) { ++retrace::verbosity; } else if (!strcmp(arg, "-w")) { wait = true; } else { std::cerr << "error: unknown option " << arg << "\n"; usage(); return 1; } } ws = glws::createNativeWindowSystem(); visual = ws->createVisual(double_buffer); for ( ; i < argc; ++i) { if (!parser.open(argv[i])) { std::cerr << "error: failed to open " << argv[i] << "\n"; return 1; } display(); parser.close(); } return 0; }
namespace glretrace { bool double_buffer = false; bool insideGlBeginEnd = false; Trace::Parser parser; glws::WindowSystem *ws = NULL; glws::Visual *visual = NULL; glws::Drawable *drawable = NULL; glws::Context *context = NULL; int window_width = 256, window_height = 256; unsigned frame = 0; long long startTime = 0; bool wait = false; bool benchmark = false; const char *compare_prefix = NULL; const char *snapshot_prefix = NULL; unsigned dump_state = ~0; void checkGlError(void) { if (benchmark || insideGlBeginEnd) { return; } GLenum error = glGetError(); if (error == GL_NO_ERROR) { return; } std::cerr << "warning: glGetError() = "; switch (error) { case GL_INVALID_ENUM: std::cerr << "GL_INVALID_ENUM"; break; case GL_INVALID_VALUE: std::cerr << "GL_INVALID_VALUE"; break; case GL_INVALID_OPERATION: std::cerr << "GL_INVALID_OPERATION"; break; case GL_STACK_OVERFLOW: std::cerr << "GL_STACK_OVERFLOW"; break; case GL_STACK_UNDERFLOW: std::cerr << "GL_STACK_UNDERFLOW"; break; case GL_OUT_OF_MEMORY: std::cerr << "GL_OUT_OF_MEMORY"; break; case GL_INVALID_FRAMEBUFFER_OPERATION: std::cerr << "GL_INVALID_FRAMEBUFFER_OPERATION"; break; case GL_TABLE_TOO_LARGE: std::cerr << "GL_TABLE_TOO_LARGE"; break; default: std::cerr << error; break; } std::cerr << "\n"; } static void snapshot(Image::Image &image) { GLint drawbuffer = double_buffer ? GL_BACK : GL_FRONT; GLint readbuffer = double_buffer ? GL_BACK : GL_FRONT; glGetIntegerv(GL_DRAW_BUFFER, &drawbuffer); glGetIntegerv(GL_READ_BUFFER, &readbuffer); glReadBuffer(drawbuffer); glReadPixels(0, 0, image.width, image.height, GL_RGBA, GL_UNSIGNED_BYTE, image.pixels); checkGlError(); glReadBuffer(readbuffer); } static void frame_complete(void) { ++frame; if (snapshot_prefix || compare_prefix) { Image::Image *ref = NULL; if (compare_prefix) { char filename[PATH_MAX]; snprintf(filename, sizeof filename, "%s%04u.png", compare_prefix, frame); ref = Image::readPNG(filename); if (!ref) { return; } if (retrace::verbosity >= 0) std::cout << "Read " << filename << "\n"; } Image::Image src(window_width, window_height, true); snapshot(src); if (snapshot_prefix) { char filename[PATH_MAX]; snprintf(filename, sizeof filename, "%s%04u.png", snapshot_prefix, frame); if (src.writePNG(filename) && retrace::verbosity >= 0) { std::cout << "Wrote " << filename << "\n"; } } if (ref) { std::cout << "Frame " << frame << " average precision of " << src.compare(*ref) << " bits\n"; delete ref; } } ws->processEvents(); } static void display(void) { Trace::Call *call; while ((call = parser.parse_call())) { const std::string &name = call->name(); if ((name[0] == 'w' && name[1] == 'g' && name[2] == 'l') || (name[0] == 'g' && name[1] == 'l' && name[2] == 'X')) { // XXX: We ignore the majority of the OS-specific calls for now if (name == "glXSwapBuffers" || name == "wglSwapBuffers") { if (retrace::verbosity >= 1) { std::cout << *call; std::cout.flush(); }; frame_complete(); if (double_buffer) drawable->swapBuffers(); else glFlush(); } else if (name == "glXMakeCurrent" || name == "wglMakeCurrent") { glFlush(); if (!double_buffer) { frame_complete(); } } else { continue; } } if (name == "glFlush") { glFlush(); if (!double_buffer) { frame_complete(); } } retrace::retrace_call(*call); if (!insideGlBeginEnd && call->no >= dump_state) { state_dump(std::cout); exit(0); } delete call; } // Reached the end of trace glFlush(); long long endTime = OS::GetTime(); float timeInterval = (endTime - startTime) * 1.0E-6; if (retrace::verbosity >= -1) { std::cout << "Rendered " << frame << " frames" " in " << timeInterval << " secs," " average of " << (frame/timeInterval) << " fps\n"; } if (wait) { while (ws->processEvents()) {} } else { exit(0); } } static void usage(void) { std::cout << "Usage: glretrace [OPTION] TRACE\n" "Replay TRACE.\n" "\n" " -b benchmark (no glgeterror; no messages)\n" " -c PREFIX compare against snapshots\n" " -db use a double buffer visual\n" " -s PREFIX take snapshots\n" " -v verbose output\n" " -D CALLNO dump state at specific call no\n" " -w wait on final frame\n"; } extern "C" int main(int argc, char **argv) { int i; for (i = 1; i < argc; ++i) { const char *arg = argv[i]; if (arg[0] != '-') { break; } if (!strcmp(arg, "--")) { break; } else if (!strcmp(arg, "-b")) { benchmark = true; retrace::verbosity = -1; } else if (!strcmp(arg, "-c")) { compare_prefix = argv[++i]; } else if (!strcmp(arg, "-D")) { dump_state = atoi(argv[++i]); retrace::verbosity = -2; } else if (!strcmp(arg, "-db")) { double_buffer = true; } else if (!strcmp(arg, "--help")) { usage(); return 0; } else if (!strcmp(arg, "-s")) { snapshot_prefix = argv[++i]; } else if (!strcmp(arg, "-v")) { ++retrace::verbosity; } else if (!strcmp(arg, "-w")) { wait = true; } else { std::cerr << "error: unknown option " << arg << "\n"; usage(); return 1; } } /* ws = glws::createNativeWindowSystem(); visual = ws->createVisual(double_buffer); drawable = ws->createDrawable(visual); drawable->resize(window_width, window_height); context = ws->createContext(visual); ws->makeCurrent(drawable, context); for ( ; i < argc; ++i) { if (parser.open(argv[i])) { startTime = OS::GetTime(); display(); parser.close(); } } */ return 0; } } /* namespace glretrace */
static void display(void) { Trace::Call *call; while ((call = parser.parse_call())) { const std::string &name = call->name(); if ((name[0] == 'w' && name[1] == 'g' && name[2] == 'l') || (name[0] == 'g' && name[1] == 'l' && name[2] == 'X')) { // XXX: We ignore the majority of the OS-specific calls for now if (name == "glXSwapBuffers" || name == "wglSwapBuffers") { if (retrace::verbosity >= 1) { std::cout << *call; std::cout.flush(); }; frame_complete(); if (double_buffer) drawable->swapBuffers(); else glFlush(); } else if (name == "glXMakeCurrent" || name == "wglMakeCurrent") { glFlush(); if (!double_buffer) { frame_complete(); } } else { continue; } } if (name == "glFlush") { glFlush(); if (!double_buffer) { frame_complete(); } } retrace::retrace_call(*call); if (!insideGlBeginEnd && call->no >= dump_state) { state_dump(std::cout); exit(0); } delete call; } // Reached the end of trace glFlush(); long long endTime = OS::GetTime(); float timeInterval = (endTime - startTime) * 1.0E-6; if (retrace::verbosity >= -1) { std::cout << "Rendered " << frame << " frames" " in " << timeInterval << " secs," " average of " << (frame/timeInterval) << " fps\n"; } if (wait) { while (ws->processEvents()) {} } else { exit(0); } }