)
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;
+}
+