)
 
 add_library (retrace_common
+    retrace.cpp
+    retrace_main.cpp
+    retrace_stdc.cpp
+)
+
+target_link_libraries (retrace_common
+    common
+    ${PNG_LIBRARIES}
+    ${ZLIB_LIBRARIES}
+    ${SNAPPY_LIBRARIES}
+)
+
+add_library (glretrace_common
     glretrace_gl.cpp
     glretrace_cgl.cpp
     glretrace_glx.cpp
     glstate_images.cpp
     glstate_params.cpp
     glstate_shaders.cpp
-    retrace.cpp
-    retrace_stdc.cpp
     glws.cpp
 )
 
-add_dependencies (retrace_common glproc)
+add_dependencies (glretrace_common glproc)
+
 
 if (WIN32 OR APPLE OR X11_FOUND)
     add_executable (glretrace
 
     target_link_libraries (glretrace
         retrace_common
+        glretrace_common
         glproc_gl
-        common
-        ${PNG_LIBRARIES}
-        ${ZLIB_LIBRARIES}
-        ${SNAPPY_LIBRARIES}
     )
 
     if (WIN32)
 
     target_link_libraries (eglretrace
         retrace_common
+        glretrace_common
         glproc_egl
-        common
-        ${PNG_LIBRARIES}
-        ${ZLIB_LIBRARIES}
-        ${SNAPPY_LIBRARIES}
         ${X11_X11_LIB}
         ${CMAKE_THREAD_LIBS_INIT}
         dl
         d3dretrace_d3d9.cpp
     )
     target_link_libraries (d3dretrace
-        common
-        ${ZLIB_LIBRARIES}
-        ${SNAPPY_LIBRARIES}
+        retrace_common
     )
     
     install (TARGETS d3dretrace RUNTIME DESTINATION bin) 
 
 #include "d3dretrace.hpp"
 
 
-namespace d3dretrace {
-
-static void display(void) {
-    retrace::Retracer retracer;
-
-    retracer.addCallbacks(d3d9_callbacks);
-
-    trace::Call *call;
-
-    while ((call = retrace::parser.parse_call())) {
-        retracer.retrace(*call);
+void
+retrace::setUp(void) {
+}
 
-        delete call;
-    }
 
-    exit(0);
+void
+retrace::addCallbacks(retrace::Retracer &retracer)
+{
+    retracer.addCallbacks(d3dretrace::d3d9_callbacks);
 }
 
 
-static void usage(void) {
-    std::cout << 
-        "Usage: d3dretrace [OPTION] TRACE\n"
-        "Replay TRACE.\n"
-        "\n"
-        "  -v           increase output verbosity\n"
-    ;
+image::Image *
+retrace::getSnapshot(void) {
+    return NULL;
 }
 
 
-extern "C"
-int main(int argc, char **argv)
+bool
+retrace::dumpState(std::ostream &os)
 {
+    return false;
+}
 
-    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, "--help")) {
-            usage();
-            return 0;
-        } else if (!strcmp(arg, "-v")) {
-            ++retrace::verbosity;
-        } else {
-            std::cerr << "error: unknown option " << arg << "\n";
-            usage();
-            return 1;
-        }
-    }
-
-    for ( ; i < argc; ++i) {
-        if (!retrace::parser.open(argv[i])) {
-            std::cerr << "error: failed to open " << argv[i] << "\n";
-            return 1;
-        }
-
-        display();
 
-        retrace::parser.close();
-    }
+void
+retrace::flushRendering(void) {
+}
 
-    return 0;
+void
+retrace::waitForInput(void) {
 }
 
-} /* namespace glretrace */
+void
+retrace::cleanUp(void) {
+}
 
 namespace glretrace {
 
 
-extern bool double_buffer;
 extern bool insideGlBeginEnd;
 extern glws::Profile defaultProfile;
 extern glws::Visual *visual[glws::PROFILE_MAX];
 extern glws::Drawable *drawable;
 extern glws::Context *context;
 
-extern unsigned frame;
-extern long long startTime;
-extern bool wait;
-
-extern bool benchmark;
-
-extern unsigned dump_state;
-
 void
 checkGlError(trace::Call &call);
 
 
 
         # Post-snapshots
         if function.name in ('glFlush', 'glFinish'):
-            print '    if (!glretrace::double_buffer) {'
+            print '    if (!retrace::doubleBuffer) {'
             print '        glretrace::frame_complete(call);'
             print '    }'
         if is_draw_array or is_draw_elements or is_misc_draw:
             print '    glretrace::insideGlBeginEnd = false;'
 
         if function.name.startswith('gl') and not function.name.startswith('glX'):
-            print r'    if (!glretrace::context && !retrace::benchmark && !retrace::profiling) {'
+            print r'    if (retrace::debug && !glretrace::context) {'
             print r'        retrace::warning(call) << "no current context\n";'
             print r'    }'
 
             print '    glretrace::insideGlBeginEnd = true;'
         elif function.name.startswith('gl'):
             # glGetError is not allowed inside glBegin/glEnd
-            print '    if (!retrace::benchmark && !retrace::profiling && !glretrace::insideGlBeginEnd) {'
+            print '    if (retrace::debug && !glretrace::insideGlBeginEnd) {'
             print '        glretrace::checkGlError(call);'
             if function.name in ('glProgramStringARB', 'glProgramStringNV'):
                 print r'        GLint error_position = -1;'
 
     it = context_map.find(ctx);
     if (it == context_map.end()) {
         glws::Context *context;
-        context_map[ctx] = context = glws::createContext(visual[glretrace::defaultProfile], sharedContext, glretrace::defaultProfile);
+        context_map[ctx] = context = glws::createContext(visual[glretrace::defaultProfile], sharedContext, glretrace::defaultProfile, retrace::debug);
         if (!sharedContext) {
             sharedContext = context;
         }
 
 static void retrace_CGLFlushDrawable(trace::Call &call) {
     if (drawable && context) {
-        if (double_buffer) {
+        if (retrace::doubleBuffer) {
             drawable->swapBuffers();
         } else {
             glFlush();
 
     }
 
 
-    glws::Context *context = glws::createContext(glretrace::visual[profile], share_context, profile);
+    glws::Context *context = glws::createContext(glretrace::visual[profile], share_context, profile, retrace::debug);
     if (!context) {
         const char *name;
         switch (profile) {
 
     if (drawable && context) {
         glFlush();
-        if (!double_buffer) {
+        if (!retrace::doubleBuffer) {
             frame_complete(call);
         }
     }
 static void retrace_eglSwapBuffers(trace::Call &call) {
     frame_complete(call);
 
-    if (double_buffer && drawable) {
+    if (retrace::doubleBuffer && drawable) {
         drawable->swapBuffers();
     } else {
         glFlush();
 
     ContextMap::const_iterator it;
     it = context_map.find(context_ptr);
     if (it == context_map.end()) {
-        return (context_map[context_ptr] = glws::createContext(visual[glretrace::defaultProfile], NULL, glretrace::defaultProfile));
+        return (context_map[context_ptr] = glws::createContext(visual[glretrace::defaultProfile], NULL, glretrace::defaultProfile, retrace::debug));
     }
 
     return it->second;
     unsigned long long orig_context = call.ret->toUIntPtr();
     glws::Context *share_context = getContext(call.arg(2).toUIntPtr());
 
-    glws::Context *context = glws::createContext(glretrace::visual[glretrace::defaultProfile], share_context, glretrace::defaultProfile);
+    glws::Context *context = glws::createContext(glretrace::visual[glretrace::defaultProfile], share_context, glretrace::defaultProfile, retrace::debug);
     context_map[orig_context] = context;
 }
 
     unsigned long long orig_context = call.ret->toUIntPtr();
     glws::Context *share_context = getContext(call.arg(2).toUIntPtr());
 
-    glws::Context *context = glws::createContext(glretrace::visual[glretrace::defaultProfile], share_context, glretrace::defaultProfile);
+    glws::Context *context = glws::createContext(glretrace::visual[glretrace::defaultProfile], share_context, glretrace::defaultProfile, retrace::debug);
     context_map[orig_context] = context;
 }
 
 
     if (drawable && context) {
         glFlush();
-        if (!double_buffer) {
+        if (!retrace::doubleBuffer) {
             frame_complete(call);
         }
     }
 
 static void retrace_glXSwapBuffers(trace::Call &call) {
     frame_complete(call);
-    if (double_buffer) {
+    if (retrace::doubleBuffer) {
         drawable->swapBuffers();
     } else {
         glFlush();
     unsigned long long orig_context = call.ret->toUIntPtr();
     glws::Context *share_context = getContext(call.arg(3).toUIntPtr());
 
-    glws::Context *context = glws::createContext(glretrace::visual[glretrace::defaultProfile], share_context, glretrace::defaultProfile);
+    glws::Context *context = glws::createContext(glretrace::visual[glretrace::defaultProfile], share_context, glretrace::defaultProfile, retrace::debug);
     context_map[orig_context] = context;
 }
 
 
     if (drawable && context) {
         glFlush();
-        if (!double_buffer) {
+        if (!retrace::doubleBuffer) {
             frame_complete(call);
         }
     }
 
 
 #include <string.h>
 
-#include "os_binary.hpp"
-#include "os_string.hpp"
-#include "os_time.hpp"
-#include "image.hpp"
 #include "retrace.hpp"
-#include "trace_callset.hpp"
 #include "glproc.hpp"
 #include "glstate.hpp"
 #include "glretrace.hpp"
 
 namespace glretrace {
 
-bool double_buffer = true;
 bool insideGlBeginEnd = false;
 glws::Profile defaultProfile = glws::PROFILE_COMPAT;
 glws::Visual *visual[glws::PROFILE_MAX];
 glws::Drawable *drawable = NULL;
 glws::Context *context = NULL;
 
-unsigned frame = 0;
-long long startTime = 0;
-bool wait = false;
-
-static const char *compare_prefix = NULL;
-static const char *snapshot_prefix = NULL;
-static trace::CallSet snapshot_frequency;
-static trace::CallSet compare_frequency;
-
-unsigned dump_state = ~0;
-
 void
 checkGlError(trace::Call &call) {
     GLenum error = glGetError();
 }
 
 
-static void
-snapshot(unsigned call_no) {
-    assert(snapshot_prefix || compare_prefix);
-
-    if (!drawable) {
-        return;
-    }
-
-    image::Image *ref = NULL;
-
-    if (compare_prefix) {
-        os::String filename = os::String::format("%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();
-    if (!src) {
-        return;
-    }
-
-    if (snapshot_prefix) {
-        if (snapshot_prefix[0] == '-' && snapshot_prefix[1] == 0) {
-            char comment[21];
-            snprintf(comment, sizeof comment, "%u", call_no);
-            src->writePNM(std::cout, comment);
-        } else {
-            os::String filename = os::String::format("%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(trace::Call &call) {
-    ++frame;
+    retrace::frameComplete(call);
 
     if (!drawable) {
         return;
 }
 
 
-static void display(void) {
-    retrace::Retracer retracer;
-
-    retracer.addCallbacks(gl_callbacks);
-    retracer.addCallbacks(glx_callbacks);
-    retracer.addCallbacks(wgl_callbacks);
-    retracer.addCallbacks(cgl_callbacks);
-    retracer.addCallbacks(egl_callbacks);
-
-    startTime = os::getTime();
-    trace::Call *call;
-
-    while ((call = retrace::parser.parse_call())) {
-        bool swapRenderTarget = call->flags & trace::CALL_FLAG_SWAP_RENDERTARGET;
-        bool doSnapshot =
-            snapshot_frequency.contains(*call) ||
-            compare_frequency.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.
-                snapshot(call->no);
-            } else {
-                // Whereas for ordinate fbo/rendertarget changes we use the
-                // previous call's number.
-                snapshot(call->no - 1);
-            }
-        }
-
-        retracer.retrace(*call);
-
-        if (doSnapshot && !swapRenderTarget) {
-            snapshot(call->no);
-        }
-
-        if (!insideGlBeginEnd &&
-            drawable && context &&
-            call->no >= dump_state) {
-            glstate::dumpCurrentContext(std::cout);
-            exit(0);
-        }
-
-        delete call;
+} /* namespace glretrace */
+
+
+void
+retrace::setUp(void) {
+    if (retrace::coreProfile) {
+        glretrace::defaultProfile = glws::PROFILE_CORE;
     }
 
-    // Reached the end of trace
-    glFlush();
+    glws::init();
 
-    long long endTime = os::getTime();
-    float timeInterval = (endTime - startTime) * (1.0 / os::timeFrequency);
+    glretrace::visual[glws::PROFILE_COMPAT] = glws::createVisual(retrace::doubleBuffer, glws::PROFILE_COMPAT);
+    glretrace::visual[glws::PROFILE_CORE] = glws::createVisual(retrace::doubleBuffer, glws::PROFILE_CORE);
+    glretrace::visual[glws::PROFILE_ES1] = glws::createVisual(retrace::doubleBuffer, glws::PROFILE_ES1);
+    glretrace::visual[glws::PROFILE_ES2] = glws::createVisual(retrace::doubleBuffer, glws::PROFILE_ES2);
+}
 
-    if ((retrace::verbosity >= -1) || (retrace::profiling)) {
-        std::cout << 
-            "Rendered " << frame << " frames"
-            " in " <<  timeInterval << " secs,"
-            " average of " << (frame/timeInterval) << " fps\n";
-    }
 
-    if (wait) {
-        while (glws::processEvents()) {}
-    } else {
-        exit(0);
-    }
+void
+retrace::addCallbacks(retrace::Retracer &retracer)
+{
+    retracer.addCallbacks(glretrace::gl_callbacks);
+    retracer.addCallbacks(glretrace::glx_callbacks);
+    retracer.addCallbacks(glretrace::wgl_callbacks);
+    retracer.addCallbacks(glretrace::cgl_callbacks);
+    retracer.addCallbacks(glretrace::egl_callbacks);
 }
 
 
-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"
-        "  -p           profiling mode (run whole trace, dump profiling info)\n"
-        "  -c PREFIX    compare against snapshots\n"
-        "  -C CALLSET   calls to compare (default is every frame)\n"
-        "  -core        use core profile\n"
-        "  -db          use a double buffer visual (default)\n"
-        "  -sb          use a single buffer visual\n"
-        "  -s PREFIX    take snapshots; `-` for PNM stdout output\n"
-        "  -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           wait on final frame\n";
+image::Image *
+retrace::getSnapshot(void) {
+    if (!glretrace::drawable) {
+        return NULL;
+    }
+
+    return glstate::getDrawBufferImage();
 }
 
-extern "C"
-int main(int argc, char **argv)
+
+bool
+retrace::dumpState(std::ostream &os)
 {
-    assert(compare_frequency.empty());
-    assert(snapshot_frequency.empty());
-
-    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")) {
-            retrace::benchmark = true;
-            retrace::verbosity = -1;
-            glws::debug = false;
-        } else if (!strcmp(arg, "-p")) {
-            retrace::profiling = true;
-            retrace::verbosity = -1;
-            glws::debug = false;
-        } else if (!strcmp(arg, "-c")) {
-            compare_prefix = argv[++i];
-            if (compare_frequency.empty()) {
-                compare_frequency = trace::CallSet(trace::FREQUENCY_FRAME);
-            }
-        } else if (!strcmp(arg, "-C")) {
-            compare_frequency = trace::CallSet(argv[++i]);
-            if (compare_prefix == NULL) {
-                compare_prefix = "";
-            }
-        } else if (!strcmp(arg, "-D")) {
-            dump_state = atoi(argv[++i]);
-            retrace::verbosity = -2;
-        } else if (!strcmp(arg, "-core")) {
-            defaultProfile = glws::PROFILE_CORE;
-        } 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.empty()) {
-                snapshot_frequency = trace::CallSet(trace::FREQUENCY_FRAME);
-            }
-            if (snapshot_prefix[0] == '-' && snapshot_prefix[1] == 0) {
-                os::setBinaryMode(stdout);
-                retrace::verbosity = -2;
-            }
-        } else if (!strcmp(arg, "-S")) {
-            snapshot_frequency = trace::CallSet(argv[++i]);
-            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;
-        }
+    if (glretrace::insideGlBeginEnd ||
+        !glretrace::drawable ||
+        !glretrace::context) {
+        return false;
     }
 
-    glws::init();
-    visual[glws::PROFILE_COMPAT] = glws::createVisual(double_buffer, glws::PROFILE_COMPAT);
-    visual[glws::PROFILE_CORE] = glws::createVisual(double_buffer, glws::PROFILE_CORE);
-    visual[glws::PROFILE_ES1] = glws::createVisual(double_buffer, glws::PROFILE_ES1);
-    visual[glws::PROFILE_ES2] = glws::createVisual(double_buffer, glws::PROFILE_ES2);
+    glstate::dumpCurrentContext(os);
 
-    for ( ; i < argc; ++i) {
-        if (!retrace::parser.open(argv[i])) {
-            std::cerr << "error: failed to open " << argv[i] << "\n";
-            return 1;
-        }
+    return true;
+}
 
-        display();
+void
+retrace::flushRendering(void) {
+    glFlush();
+}
 
-        retrace::parser.close();
+void
+retrace::waitForInput(void) {
+    while (glws::processEvents()) {
     }
+}
 
+void
+retrace::cleanUp(void) {
     for (int n = 0; n < glws::PROFILE_MAX; n++) {
-        delete visual[n];
+        delete glretrace::visual[n];
     }
 
     glws::cleanup();
-
-    return 0;
 }
-
-} /* namespace glretrace */
 
 
 static void retrace_wglCreateContext(trace::Call &call) {
     unsigned long long orig_context = call.ret->toUIntPtr();
-    glws::Context *context = glws::createContext(glretrace::visual[glretrace::defaultProfile], NULL, glretrace::defaultProfile);
+    glws::Context *context = glws::createContext(glretrace::visual[glretrace::defaultProfile], NULL, glretrace::defaultProfile, retrace::debug);
     context_map[orig_context] = context;
 }
 
 static void retrace_wglMakeCurrent(trace::Call &call) {
     if (drawable && context) {
         glFlush();
-        if (!double_buffer) {
+        if (!retrace::doubleBuffer) {
             frame_complete(call);
         }
     }
 
 static void retrace_wglSwapBuffers(trace::Call &call) {
     frame_complete(call);
-    if (double_buffer) {
+    if (retrace::doubleBuffer) {
         drawable->swapBuffers();
     } else {
         glFlush();
     glws::Context *old_context = context_map[hglrc2];
 
     glws::Context *new_context =
-        glws::createContext(old_context->visual, share_context, glretrace::defaultProfile);
+        glws::createContext(old_context->visual, share_context, glretrace::defaultProfile, retrace::debug);
     if (new_context) {
         if (context == old_context) {
             glws::makeCurrent(drawable, new_context);
         share_context = context_map[call.arg(1).toUIntPtr()];
     }
 
-    glws::Context *context = glws::createContext(glretrace::visual[glretrace::defaultProfile], share_context, glretrace::defaultProfile);
+    glws::Context *context = glws::createContext(glretrace::visual[glretrace::defaultProfile], share_context, glretrace::defaultProfile, retrace::debug);
     context_map[orig_context] = context;
 }
 
 
 namespace glws {
 
 
-bool debug = true;
-
-
 bool
 checkExtension(const char *extName, const char *extString)
 {
 
 };
 
 
-extern bool debug;
-
-
 bool
 checkExtension(const char *extName, const char *extString);
 
 createDrawable(const Visual *visual, int width = 32, int height = 32);
 
 Context *
-createContext(const Visual *visual, Context *shareContext = 0, Profile profile = PROFILE_COMPAT);
+createContext(const Visual *visual, Context *shareContext = 0, Profile profile = PROFILE_COMPAT, bool debug = false);
 
 bool
 makeCurrent(Drawable *drawable, Context *context);
 
 }
 
 Context *
-createContext(const Visual *visual, Context *shareContext, Profile profile)
+createContext(const Visual *visual, Context *shareContext, Profile profile, bool debug)
 {
     NSOpenGLPixelFormat *pixelFormat = static_cast<const CocoaVisual *>(visual)->pixelFormat;
     NSOpenGLContext *share_context = nil;
 
 }
 
 Context *
-createContext(const Visual *_visual, Context *shareContext, Profile profile)
+createContext(const Visual *_visual, Context *shareContext, Profile profile, bool debug)
 {
     const EglVisual *visual = static_cast<const EglVisual *>(_visual);
     EGLContext share_context = EGL_NO_CONTEXT;
 
 }
 
 Context *
-createContext(const Visual *_visual, Context *shareContext, Profile profile)
+createContext(const Visual *_visual, Context *shareContext, Profile profile, bool debug)
 {
     const GlxVisual *visual = static_cast<const GlxVisual *>(_visual);
     GLXContext share_context = NULL;
 
 }
 
 Context *
-createContext(const Visual *visual, Context *shareContext, Profile profile)
+createContext(const Visual *visual, Context *shareContext, Profile profile, bool debug)
 {
     if (profile != PROFILE_COMPAT) {
         return NULL;
 
 namespace retrace {
 
 
-trace::Parser parser;
-
-
-int verbosity = 0;
-bool benchmark = false;
-bool profiling = false;
-
-
 static bool call_dumped = false;
 
 
 
 #include "trace_parser.hpp"
 
 
+namespace image {
+    class Image;
+}
+
+
 namespace retrace {
 
 
 extern int verbosity;
 
 /**
- * Avoid expensive checks when benchmarking.
+ * Debugging checks.
  */
-extern bool benchmark;
+extern bool debug;
 
 /**
  * Add profiling data to the dump when retracing.
 extern bool profiling;
 
 
+extern bool doubleBuffer;
+extern bool coreProfile;
+
+
 std::ostream &warning(trace::Call &call);
 
 
 };
 
 
+void
+setUp(void);
+
+void
+addCallbacks(retrace::Retracer &retracer);
+
+void
+frameComplete(trace::Call &call);
+
+image::Image *
+getSnapshot(void);
+
+bool
+dumpState(std::ostream &os);
+
+void
+flushRendering(void);
+
+void
+waitForInput(void);
+
+void
+cleanUp(void);
+
+
 } /* namespace retrace */
 
 #endif /* _RETRACE_HPP_ */
 
--- /dev/null
+/**************************************************************************
+ *
+ * Copyright 2011 Jose Fonseca
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#include <string.h>
+#include <iostream>
+
+#include "os_binary.hpp"
+#include "os_time.hpp"
+#include "image.hpp"
+#include "trace_callset.hpp"
+#include "trace_dump.hpp"
+#include "retrace.hpp"
+
+
+static bool wait = false;
+
+static const char *comparePrefix = NULL;
+static const char *snapshotPrefix = NULL;
+static trace::CallSet snapshotFrequency;
+static trace::CallSet compareFrequency;
+
+static unsigned dumpStateCallNo = ~0;
+
+
+namespace retrace {
+
+
+trace::Parser parser;
+
+
+int verbosity = 0;
+bool debug = true;
+bool profiling = false;
+
+
+bool doubleBuffer = true;
+bool coreProfile = false;
+
+
+static unsigned frameNo = 0;
+
+
+void
+frameComplete(trace::Call &call) {
+    ++frameNo;
+}
+
+
+static void
+takeSnapshot(unsigned call_no) {
+    assert(snapshotPrefix || comparePrefix);
+
+    image::Image *ref = NULL;
+
+    if (comparePrefix) {
+        os::String filename = os::String::format("%s%010u.png", comparePrefix, call_no);
+        ref = image::readPNG(filename);
+        if (!ref) {
+            return;
+        }
+        if (retrace::verbosity >= 0) {
+            std::cout << "Read " << filename << "\n";
+        }
+    }
+
+    image::Image *src = getSnapshot();
+    if (!src) {
+        return;
+    }
+
+    if (snapshotPrefix) {
+        if (snapshotPrefix[0] == '-' && snapshotPrefix[1] == 0) {
+            char comment[21];
+            snprintf(comment, sizeof comment, "%u", call_no);
+            src->writePNM(std::cout, comment);
+        } else {
+            os::String filename = os::String::format("%s%010u.png", snapshotPrefix, 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;
+
+    return;
+}
+
+
+static void
+mainLoop() {
+    retrace::Retracer retracer;
+
+    addCallbacks(retracer);
+
+    long long startTime = 0; 
+
+    startTime = os::getTime();
+    trace::Call *call;
+
+    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);
+            }
+        }
+
+        retracer.retrace(*call);
+
+        if (doSnapshot && !swapRenderTarget) {
+            takeSnapshot(call->no);
+        }
+
+        if (call->no >= dumpStateCallNo &&
+            dumpState(std::cout)) {
+            exit(0);
+        }
+
+        delete call;
+    }
+
+    // Reached the end of trace
+    flushRendering();
+
+    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 (wait) {
+        waitForInput();
+    } else {
+        exit(0);
+    }
+}
+
+
+} /* namespace retrace */
+
+
+static void
+usage(const char *argv0) {
+    std::cout << 
+        "Usage: " << argv0 << " [OPTION] TRACE\n"
+        "Replay TRACE.\n"
+        "\n"
+        "  -b           benchmark mode (no error checking or warning messages)\n"
+        "  -p           profiling mode (run whole trace, dump profiling info)\n"
+        "  -c PREFIX    compare against snapshots\n"
+        "  -C CALLSET   calls to compare (default is every frame)\n"
+        "  -core        use core profile\n"
+        "  -db          use a double buffer visual (default)\n"
+        "  -sb          use a single buffer visual\n"
+        "  -s PREFIX    take snapshots; `-` for PNM stdout output\n"
+        "  -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           wait on final frame\n";
+}
+
+
+extern "C"
+int main(int argc, char **argv)
+{
+    assert(compareFrequency.empty());
+    assert(snapshotFrequency.empty());
+
+    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")) {
+            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()) {
+                compareFrequency = trace::CallSet(trace::FREQUENCY_FRAME);
+            }
+        } else if (!strcmp(arg, "-C")) {
+            compareFrequency = trace::CallSet(argv[++i]);
+            if (comparePrefix == NULL) {
+                comparePrefix = "";
+            }
+        } else if (!strcmp(arg, "-D")) {
+            dumpStateCallNo = atoi(argv[++i]);
+            retrace::verbosity = -2;
+        } else if (!strcmp(arg, "-core")) {
+            retrace::coreProfile = true;
+        } else if (!strcmp(arg, "-db")) {
+            retrace::doubleBuffer = true;
+        } else if (!strcmp(arg, "-sb")) {
+            retrace::doubleBuffer = false;
+        } else if (!strcmp(arg, "--help")) {
+            usage(argv[0]);
+            return 0;
+        } else if (!strcmp(arg, "-s")) {
+            snapshotPrefix = argv[++i];
+            if (snapshotFrequency.empty()) {
+                snapshotFrequency = trace::CallSet(trace::FREQUENCY_FRAME);
+            }
+            if (snapshotPrefix[0] == '-' && snapshotPrefix[1] == 0) {
+                os::setBinaryMode(stdout);
+                retrace::verbosity = -2;
+            }
+        } else if (!strcmp(arg, "-S")) {
+            snapshotFrequency = trace::CallSet(argv[++i]);
+            if (snapshotPrefix == NULL) {
+                snapshotPrefix = "";
+            }
+        } else if (!strcmp(arg, "-v")) {
+            ++retrace::verbosity;
+        } else if (!strcmp(arg, "-w")) {
+            wait = true;
+        } else {
+            std::cerr << "error: unknown option " << arg << "\n";
+            usage(argv[0]);
+            return 1;
+        }
+    }
+
+    retrace::setUp();
+
+    for ( ; i < argc; ++i) {
+        if (!retrace::parser.open(argv[i])) {
+            std::cerr << "error: failed to open " << argv[i] << "\n";
+            return 1;
+        }
+
+        retrace::mainLoop();
+
+        retrace::parser.close();
+    }
+
+    retrace::cleanUp();
+
+    return 0;
+}
+