cmake_minimum_required (VERSION 2.8)
+
+# Use clang on MacOSX. gcc doesn't support __thread key, and Apple has
+# abandoned it for clang. This must be done before the project is defined.
+if (APPLE)
+ set (CMAKE_C_COMPILER "clang")
+ set (CMAKE_CXX_COMPILER "clang++")
+endif ()
+
+
project (apitrace)
##############################################################################
# Find dependencies
+# Ensure __thread is support
+if (NOT MSVC)
+ include (CheckCXXSourceCompiles)
+ check_cxx_source_compiles("__thread int i; int main() { return 0; }" HAVE_COMPILER_TLS)
+ if (NOT HAVE_COMPILER_TLS)
+ if (APPLE)
+ message (FATAL_ERROR "C++ compiler does not support __thread keyword. Please install XCode 4.5 or higher.")
+ else (MINGW32)
+ message (FATAL_ERROR "C++ compiler does not support __thread keyword. Please use MinGW g++ version 4.4 or higher")
+ else ()
+ message (FATAL_ERROR "C++ compiler does not support __thread keyword.")
+ endif ()
+ endif ()
+endif ()
+
set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
set (CMAKE_USE_PYTHON_VERSION 2.7 2.6)
/**************************************************************************
*
- * Copyright 2011 Jose Fonseca
+ * Copyright 2011-2012 Jose Fonseca
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
#include <pthread.h>
#endif
+
+/**
+ * Compiler TLS.
+ *
+ * See also:
+ * - http://gcc.gnu.org/onlinedocs/gcc-4.6.3/gcc/Thread_002dLocal.html
+ * - http://msdn.microsoft.com/en-us/library/9w1sdazb.aspx
+ */
+#if defined(_MSC_VER)
+# define thread_specific __declspec(thread)
+#elif defined(__GNUC__)
+# define thread_specific __thread
+#else
+# define thread_specific
+# error "Unsupported compiler"
+#endif
+
+
namespace os {
- class recursive_mutex
+ /**
+ * Base class for mutex and recursive_mutex.
+ */
+ class _base_mutex
{
public:
#ifdef _WIN32
typedef pthread_mutex_t native_handle_type;
#endif
- recursive_mutex(void) {
+ _base_mutex(void) {
#ifdef _WIN32
InitializeCriticalSection(&_native_handle);
#else
#endif
}
- ~recursive_mutex() {
+ ~_base_mutex() {
#ifdef _WIN32
DeleteCriticalSection(&_native_handle);
#else
#endif
}
- private:
+ native_handle_type & native_handle() {
+ return _native_handle;
+ }
+
+ protected:
native_handle_type _native_handle;
};
+ /**
+ * Same interface as std::mutex.
+ */
+ class mutex : public _base_mutex
+ {
+ public:
+ inline
+ mutex(void) {
+#ifdef _WIN32
+ InitializeCriticalSection(&_native_handle);
+#else
+ pthread_mutex_init(&_native_handle, NULL);
+#endif
+ }
+ };
+
+
+ /**
+ * Same interface as std::recursive_mutex.
+ */
+ class recursive_mutex : public _base_mutex
+ {
+ public:
+ inline
+ recursive_mutex(void) {
+#ifdef _WIN32
+ InitializeCriticalSection(&_native_handle);
+#else
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&_native_handle, &attr);
+ pthread_mutexattr_destroy(&attr);
+#endif
+ }
+ };
+
+
+ /**
+ * Same interface as std::unique_lock;
+ */
+ template< class Mutex >
+ class unique_lock
+ {
+ public:
+ typedef Mutex mutex_type;
+
+ inline explicit
+ unique_lock(mutex_type & mutex) :
+ _mutex(&mutex)
+ {
+ _mutex->lock();
+ }
+
+ inline
+ ~unique_lock() {
+ _mutex->unlock();
+ }
+
+ inline void
+ lock() {
+ _mutex->lock();
+ }
+
+ inline void
+ unlock() {
+ _mutex->unlock();
+ }
+
+ mutex_type *
+ mutex() const {
+ return _mutex;
+ }
+
+ protected:
+ mutex_type *_mutex;
+ };
+
+
+ /**
+ * Same interface as std::condition_variable
+ */
+ class condition_variable
+ {
+ public:
+#ifdef _WIN32
+ typedef CONDITION_VARIABLE native_handle_type;
+#else
+ typedef pthread_cond_t native_handle_type;
+#endif
+
+ condition_variable() {
+#ifdef _WIN32
+ InitializeConditionVariable(&_native_handle);
+#else
+ pthread_cond_init(&_native_handle, NULL);
+#endif
+ }
+
+ ~condition_variable() {
+#ifdef _WIN32
+ /* No-op */
+#else
+ pthread_cond_destroy(&_native_handle);
+#endif
+ }
+
+ inline void
+ signal(void) {
+#ifdef _WIN32
+ WakeConditionVariable(&_native_handle);
+#else
+ pthread_cond_signal(&_native_handle);
+#endif
+ }
+
+ inline void
+ wait(unique_lock<mutex> & lock) {
+ mutex::native_handle_type & mutex_native_handle = lock.mutex()->native_handle();
+#ifdef _WIN32
+ /* FIXME */
+ SleepConditionVariableCS(&_native_handle, &mutex_native_handle, INFINITE);
+#else
+ pthread_cond_wait(&_native_handle, &mutex_native_handle);
+#endif
+ }
+
+ protected:
+ native_handle_type _native_handle;
+ };
+
+
+ /**
+ * Same interface as boost::thread_specific_ptr.
+ */
template <typename T>
class thread_specific_ptr
{
void reset(T* new_value=0) {
T * old_value = get();
+ set(new_value);
+ if (old_value) {
+ delete old_value;
+ }
+ }
+
+ T* release (void) {
+ T * old_value = get();
+ set(0);
+ return old_value;
+ }
+
+private:
+ void set(T* new_value) {
#ifdef _WIN32
TlsSetValue(dwTlsIndex, new_value);
#else
pthread_setspecific(key, new_value);
#endif
- if (old_value) {
- delete old_value;
- }
}
};
+
+ /**
+ * Same interface as std::thread
+ */
+ class thread {
+ public:
+#ifdef _WIN32
+ typedef HANDLE native_handle_type;
+#else
+ typedef pthread_t native_handle_type;
+#endif
+
+ template< class Function, class Arg >
+ explicit thread( Function& f, Arg arg ) {
+#ifdef _WIN32
+ /* FIXME */
+ DWORD id = 0;
+ _native_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)f, (LPVOID)arg, 0, &id);
+#else
+ pthread_create(&_native_handle, NULL, ( void *(*) (void *))f, arg);
+#endif
+ }
+
+ inline void
+ join() {
+#ifdef _WIN32
+ WaitForSingleObject(_native_handle, INFINITE);
+#else
+ pthread_join(_native_handle, NULL);
+#endif
+ }
+
+ private:
+ native_handle_type _native_handle;
+
+#if 0
+#ifdef _WIN32
+ template< class Function, class Arg >
+ static DWORD WINAPI
+ ThreadProc(LPVOID lpParameter) {
+
+ );
+#endif
+#endif
+ };
+
} /* namespace os */
#endif /* _OS_THREAD_HPP_ */
#endif
}
-static unsigned next_thread_id = 0;
-static os::thread_specific_ptr<unsigned> thread_id_specific_ptr;
+static unsigned next_thread_num = 1;
+static thread_specific unsigned thread_num = 0;
unsigned LocalWriter::beginEnter(const FunctionSig *sig) {
mutex.lock();
open();
}
- unsigned *thread_id_ptr = thread_id_specific_ptr.get();
- unsigned thread_id;
- if (thread_id_ptr) {
- thread_id = *thread_id_ptr;
- } else {
- thread_id = next_thread_id++;
- thread_id_ptr = new unsigned;
- *thread_id_ptr = thread_id;
- thread_id_specific_ptr.reset(thread_id_ptr);
+ unsigned this_thread_num = thread_num;
+ if (!this_thread_num) {
+ this_thread_num = thread_num = next_thread_num++;
}
+ assert(thread_num > 0);
+ unsigned thread_id = thread_num - 1;
return Writer::beginEnter(sig, thread_id);
}
{
}
- ~Context()
- {
- delete wsContext;
- }
+ ~Context();
glws::Context* wsContext;
extern bool insideGlBeginEnd;
-extern Context *currentContext;
+Context *
+getCurrentContext(void);
int
print ' glretrace::insideGlBeginEnd = false;'
if function.name.startswith('gl') and not function.name.startswith('glX'):
- print r' if (retrace::debug && !glretrace::currentContext) {'
+ print r' if (retrace::debug && !glretrace::getCurrentContext()) {'
print r' retrace::warning(call) << "no current context\n";'
print r' }'
)
if function.name in ('glUseProgram', 'glUseProgramObjectARB'):
- print r' if (glretrace::currentContext) {'
- print r' glretrace::currentContext->activeProgram = call.arg(0).toUInt();'
+ print r' glretrace::Context *currentContext = glretrace::getCurrentContext();'
+ print r' if (currentContext) {'
+ print r' currentContext->activeProgram = call.arg(0).toUInt();'
print r' }'
# Only profile if not inside a list as the queries get inserted into list
# Error checking
if function.name.startswith('gl'):
# glGetError is not allowed inside glBegin/glEnd
- print ' if (retrace::debug && !glretrace::insideGlBeginEnd) {'
+ print ' if (retrace::debug && !glretrace::insideGlBeginEnd && glretrace::getCurrentContext()) {'
print ' glretrace::checkGlError(call);'
if function.name in ('glProgramStringARB', 'glProgramStringNV'):
print r' GLint error_position = -1;'
print ' GLint program = -1;'
print ' if (glretrace::insideList) {'
print ' // glUseProgram & glUseProgramObjectARB are display-list-able'
- print ' program = _program_map[glretrace::currentContext->activeProgram];'
+ print r' glretrace::Context *currentContext = glretrace::getCurrentContext();'
+ print ' program = _program_map[currentContext->activeProgram];'
print ' } else {'
print ' GLint pipeline = 0;'
print ' if (_pipelineHasBeenBound) {'
Context *context = getContext(ctx);
if (context) {
- if (retrace::doubleBuffer) {
- context->drawable->swapBuffers();
+ if (context->drawable) {
+ if (retrace::doubleBuffer) {
+ context->drawable->swapBuffers();
+ } else {
+ glFlush();
+ }
+ frame_complete(call);
} else {
- glFlush();
+ if (retrace::debug) {
+ retrace::warning(call) << "context has no drawable\n";
+ }
}
-
- frame_complete(call);
}
}
GLvoid * pixels = NULL;
- if (glretrace::currentContext != context) {
+ if (glretrace::getCurrentContext() != context) {
if (retrace::debug) {
retrace::warning(call) << "current context mismatch\n";
}
it = drawable_map.find(orig_surface);
if (it != drawable_map.end()) {
+ glretrace::Context *currentContext = glretrace::getCurrentContext();
if (!currentContext || it->second != currentContext->drawable) {
// TODO: reference count
delete it->second;
it = context_map.find(orig_context);
if (it != context_map.end()) {
+ glretrace::Context *currentContext = glretrace::getCurrentContext();
if (it->second != currentContext) {
// TODO: reference count
delete it->second;
void
beginProfile(trace::Call &call, bool isDraw) {
+ glretrace::Context *currentContext = glretrace::getCurrentContext();
+
/* Create call query */
CallQuery query;
query.isDraw = isDraw;
query.call = call.no;
query.sig = call.sig;
- query.program = glretrace::currentContext ? glretrace::currentContext->activeProgram : 0;
+ query.program = currentContext ? currentContext->activeProgram : 0;
/* GPU profiling only for draw calls */
if (isDraw) {
void
initContext() {
+ glretrace::Context *currentContext = glretrace::getCurrentContext();
+
/* Ensure we have adequate extension support */
assert(currentContext);
supportsTimestamp = currentContext->hasExtension("GL_ARB_timer_query");
/* Setup debug message call back */
if (retrace::debug && supportsDebugOutput) {
+ glretrace::Context *currentContext = glretrace::getCurrentContext();
glDebugMessageCallbackARB(&debugOutputCallback, currentContext);
if (DEBUG_OUTPUT_SYNCHRONOUS) {
retrace::frameComplete(call);
+ glretrace::Context *currentContext = glretrace::getCurrentContext();
if (!currentContext) {
return;
}
+ assert(currentContext->drawable);
if (retrace::debug && !currentContext->drawable->visible) {
retrace::warning(call) << "could not infer drawable size (glViewport never called)\n";
}
image::Image *
retrace::getSnapshot(void) {
- if (!glretrace::currentContext) {
+ if (!glretrace::getCurrentContext()) {
return NULL;
}
bool
retrace::dumpState(std::ostream &os)
{
+ glretrace::Context *currentContext = glretrace::getCurrentContext();
+
if (glretrace::insideGlBeginEnd ||
- !glretrace::currentContext) {
+ !currentContext) {
return false;
}
void
retrace::flushRendering(void) {
- glretrace::flushQueries();
- glFlush();
+ glretrace::Context *currentContext = glretrace::getCurrentContext();
+ if (currentContext) {
+ glretrace::flushQueries();
+ glFlush();
+ }
}
void
if (retrace::doubleBuffer) {
if (drawable) {
drawable->swapBuffers();
- } else if (currentContext) {
- currentContext->drawable->swapBuffers();
+ } else {
+ glretrace::Context *currentContext = glretrace::getCurrentContext();
+ if (currentContext) {
+ currentContext->drawable->swapBuffers();
+ }
}
} else {
glFlush();
Context *new_context = glretrace::createContext(share_context);
if (new_context) {
+ glretrace::Context *currentContext = glretrace::getCurrentContext();
if (currentContext == old_context) {
glretrace::makeCurrent(call, currentContext->drawable, new_context);
}
#include <string.h>
+#include "os_thread.hpp"
#include "retrace.hpp"
#include "glproc.hpp"
#include "glstate.hpp"
namespace glretrace {
-Context *currentContext = NULL;
-
-
static glws::Visual *
visuals[glws::PROFILE_MAX];
}
+Context::~Context()
+{
+ //assert(this != getCurrentContext());
+ if (this != getCurrentContext()) {
+ delete wsContext;
+ }
+}
+
+
+static thread_specific Context *
+currentContextPtr;
+
+
bool
makeCurrent(trace::Call &call, glws::Drawable *drawable, Context *context)
{
+ Context *currentContext = currentContextPtr;
glws::Drawable *currentDrawable = currentContext ? currentContext->drawable : NULL;
if (drawable == currentDrawable && context == currentContext) {
return false;
}
- if (currentContext) {
- currentContext->drawable = NULL;
- }
+ currentContextPtr = context;
if (drawable && context) {
- currentContext = context;
- currentContext->drawable = drawable;
+ context->drawable = drawable;
if (!context->used) {
initContext();
context->used = true;
}
- } else {
- currentContext = NULL;
}
return true;
}
+Context *
+getCurrentContext(void) {
+ return currentContextPtr;
+}
/**
*/
void
updateDrawable(int width, int height) {
+ Context *currentContext = getCurrentContext();
if (!currentContext) {
return;
}
* - http://developer.apple.com/library/mac/#samplecode/glut/Introduction/Intro.html
* - http://developer.apple.com/library/mac/#samplecode/GLEssentials/Introduction/Intro.html
* - http://www.glfw.org/
+ * - http://cocoasamurai.blogspot.co.uk/2008/04/guide-to-threading-on-leopard.html
+ * - http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/Multithreading/Introduction/Introduction.html
*/
#include "glws.hpp"
+/**
+ * Dummy thread to force Cocoa to enter multithreading mode.
+ */
+@interface DummyThread : NSObject
+ + (void)enterMultiThreaded;
+ + (void)dummyThreadMethod:(id)unused;
+@end
+
+@implementation DummyThread
+ + (void)dummyThreadMethod:(id)unused {
+ (void)unused;
+ }
+
+ + (void)enterMultiThreaded {
+ [NSThread detachNewThreadSelector:@selector(dummyThreadMethod:)
+ toTarget:self
+ withObject:nil];
+ }
+@end
+
+
namespace glws {
-NSAutoreleasePool *autoreleasePool = nil;
+static __thread NSAutoreleasePool *
+autoreleasePool = nil;
class CocoaVisual : public Visual
};
+static inline void
+initThread(void) {
+ if (autoreleasePool == nil) {
+ autoreleasePool = [[NSAutoreleasePool alloc] init];
+ }
+}
+
void
init(void) {
// Prevent glproc to load system's OpenGL, so that we can trace glretrace.
_libGlHandle = dlopen("OpenGL", RTLD_LOCAL | RTLD_NOW | RTLD_FIRST);
- [NSApplication sharedApplication];
+ initThread();
+
+ [DummyThread enterMultiThreaded];
- autoreleasePool = [[NSAutoreleasePool alloc] init];
+ bool isMultiThreaded = [NSThread isMultiThreaded];
+ if (!isMultiThreaded) {
+ std::cerr << "error: failed to enable Cocoa multi-threading\n";
+ exit(1);
+ }
+
+ [NSApplication sharedApplication];
[NSApp finishLaunching];
}
Visual *
createVisual(bool doubleBuffer, Profile profile) {
+
+ initThread();
+
if (profile != PROFILE_COMPAT &&
profile != PROFILE_CORE) {
return nil;
Drawable *
createDrawable(const Visual *visual, int width, int height, bool pbuffer)
{
+ initThread();
+
return new CocoaDrawable(visual, width, height, pbuffer);
}
Context *
createContext(const Visual *visual, Context *shareContext, Profile profile, bool debug)
{
+ initThread();
+
NSOpenGLPixelFormat *pixelFormat = static_cast<const CocoaVisual *>(visual)->pixelFormat;
NSOpenGLContext *share_context = nil;
NSOpenGLContext *context;
bool
makeCurrent(Drawable *drawable, Context *context)
{
+ initThread();
+
if (!drawable || !context) {
[NSOpenGLContext clearCurrentContext];
} else {
bool
processEvents(void) {
- NSEvent* event;
+ initThread();
+ NSEvent* event;
do {
event = [NSApp nextEventMatchingMask:NSAnyEventMask
untilDate:[NSDate distantPast]
void
init(void) {
+ XInitThreads();
+
display = XOpenDisplay(NULL);
if (!display) {
std::cerr << "error: unable to open display " << XDisplayName(NULL) << "\n";
#include "os_binary.hpp"
#include "os_time.hpp"
+#include "os_thread.hpp"
#include "image.hpp"
#include "trace_callset.hpp"
#include "trace_dump.hpp"
static unsigned dumpStateCallNo = ~0;
+retrace::Retracer retracer;
+
namespace retrace {
}
+class RelayRunner;
+
+
static void
-mainLoop() {
- retrace::Retracer retracer;
+retraceCall(trace::Call *call) {
+ bool swapRenderTarget = call->flags &
+ trace::CALL_FLAG_SWAP_RENDERTARGET;
+ bool doSnapshot = snapshotFrequency.contains(*call) ||
+ compareFrequency.contains(*call);
+
+ // For calls which cause rendertargets to be swaped, we take the
+ // snapshot _before_ swapping the rendertargets.
+ if (doSnapshot && swapRenderTarget) {
+ if (call->flags & trace::CALL_FLAG_END_FRAME) {
+ // For swapbuffers/presents we still use this
+ // call number, spite not have been executed yet.
+ takeSnapshot(call->no);
+ } else {
+ // Whereas for ordinate fbo/rendertarget changes we
+ // use the previous call's number.
+ takeSnapshot(call->no - 1);
+ }
+ }
- addCallbacks(retracer);
+ callNo = call->no;
+ retracer.retrace(*call);
- long long startTime = 0;
- frameNo = 0;
+ if (doSnapshot && !swapRenderTarget)
+ takeSnapshot(call->no);
- startTime = os::getTime();
- trace::Call *call;
+ if (call->no >= dumpStateCallNo &&
+ dumpState(std::cout)) {
+ exit(0);
+ }
+}
- while ((call = retrace::parser.parse_call())) {
- bool swapRenderTarget = call->flags & trace::CALL_FLAG_SWAP_RENDERTARGET;
- bool doSnapshot =
- snapshotFrequency.contains(*call) ||
- compareFrequency.contains(*call)
- ;
-
- // For calls which cause rendertargets to be swaped, we take the
- // snapshot _before_ swapping the rendertargets.
- if (doSnapshot && swapRenderTarget) {
- if (call->flags & trace::CALL_FLAG_END_FRAME) {
- // For swapbuffers/presents we still use this call number,
- // spite not have been executed yet.
- takeSnapshot(call->no);
- } else {
- // Whereas for ordinate fbo/rendertarget changes we use the
- // previous call's number.
- takeSnapshot(call->no - 1);
+
+class RelayRace
+{
+public:
+ std::vector<RelayRunner*> runners;
+
+ RelayRace();
+
+ RelayRunner *
+ getRunner(unsigned leg);
+
+ void
+ startRace(void);
+
+ void
+ passBaton(trace::Call *call);
+
+ void
+ finishRace();
+};
+
+
+class RelayRunner
+{
+public:
+ RelayRace *race;
+ unsigned leg;
+ os::mutex mutex;
+ os::condition_variable wake_cond;
+
+ bool finished;
+ trace::Call *baton;
+ os::thread *thread;
+
+ static void *
+ runnerThread(RelayRunner *_this);
+
+ RelayRunner(RelayRace *race, unsigned _leg) :
+ race(race),
+ leg(_leg),
+ finished(false),
+ baton(0),
+ thread(0)
+ {
+ if (leg) {
+ thread = new os::thread(runnerThread, this);
+ }
+ }
+
+ void
+ runRace(void) {
+ os::unique_lock<os::mutex> lock(mutex);
+
+ while (1) {
+ while (!finished && !baton) {
+ wake_cond.wait(lock);
}
+
+ if (finished) {
+ break;
+ }
+
+ assert(baton);
+ trace::Call *call = baton;
+ baton = 0;
+
+ runLeg(call);
}
- callNo = call->no;
- retracer.retrace(*call);
+ if (0) std::cerr << "leg " << leg << " actually finishing\n";
- if (doSnapshot && !swapRenderTarget) {
- takeSnapshot(call->no);
+ if (leg == 0) {
+ std::vector<RelayRunner*>::iterator it;
+ for (it = race->runners.begin() + 1; it != race->runners.end(); ++it) {
+ RelayRunner* runner = *it;
+ runner->finishRace();
+ }
}
+ }
- if (call->no >= dumpStateCallNo &&
- dumpState(std::cout)) {
- exit(0);
+ void runLeg(trace::Call *call) {
+ do {
+ assert(call);
+ assert(call->thread_id == leg);
+ retraceCall(call);
+ delete call;
+ call = parser.parse_call();
+ } while (call && call->thread_id == leg);
+
+ if (call) {
+ assert(call->thread_id != leg);
+ flushRendering();
+ race->passBaton(call);
+ } else {
+ if (0) std::cerr << "finished on leg " << leg << "\n";
+ if (leg) {
+ race->finishRace();
+ } else {
+ finished = true;
+ }
}
+ }
+
+ void receiveBaton(trace::Call *call) {
+ assert (call->thread_id == leg);
- delete call;
+ mutex.lock();
+ baton = call;
+ mutex.unlock();
+
+ wake_cond.signal();
}
- // Reached the end of trace
- flushRendering();
+ 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));
+}
+
+RelayRunner *
+RelayRace::getRunner(unsigned leg) {
+ RelayRunner *runner;
+
+ if (leg >= runners.size()) {
+ runners.resize(leg + 1);
+ runner = 0;
+ } else {
+ runner = runners[leg];
+ }
+ if (!runner) {
+ runner = new RelayRunner(this, leg);
+ runners[leg] = runner;
+ }
+ return runner;
+}
+
+void
+RelayRace::startRace(void) {
+ trace::Call *call;
+ call = parser.parse_call();
+
+ if (!call) {
+ return;
+ }
+
+ assert(call->thread_id == 0);
+
+ RelayRunner *foreRunner = getRunner(0);
+ if (call->thread_id == 0) {
+ foreRunner->baton = call;
+ } else {
+ passBaton(call);
+ }
+
+ foreRunner->runRace();
+}
+
+void
+RelayRace::passBaton(trace::Call *call) {
+ if (0) std::cerr << "switching to thread " << call->thread_id << "\n";
+ RelayRunner *runner = getRunner(call->thread_id);
+ runner->receiveBaton(call);
+}
+
+void
+RelayRace::finishRace(void) {
+ RelayRunner *runner = getRunner(0);
+ runner->finishRace();
+}
+
+
+static void
+mainLoop() {
+ addCallbacks(retracer);
+
+ long long startTime = 0;
+ frameNo = 0;
+
+ startTime = os::getTime();
+
+ RelayRace race;
+ race.startRace();
long long endTime = os::getTime();
float timeInterval = (endTime - startTime) * (1.0 / os::timeFrequency);
GLlocation = Handle("location", GLint, key=('program', GLhandleARB))
GLlocationARB = Handle("location", GLint, key=('programObj', GLhandleARB))
-contextKey = ('reinterpret_cast<uintptr_t>(glretrace::currentContext)', UIntPtr)
+contextKey = ('reinterpret_cast<uintptr_t>(glretrace::getCurrentContext())', UIntPtr)
GLprogramARB = Handle("programARB", GLuint)
GLframebuffer = Handle("framebuffer", GLuint)
}
};
-static os::thread_specific_ptr<ThreadState> thread_state;
+static thread_specific ThreadState *thread_state;
static ThreadState *get_ts(void)
{
- ThreadState *ts = thread_state.get();
-
+ ThreadState *ts = thread_state;
if (!ts) {
- ts = new ThreadState;
- thread_state.reset(ts);
+ thread_state = ts = new ThreadState;
}
return ts;