]> git.cworth.org Git - apitrace/commitdiff
Merge remote-tracking branch 'github/master' into mt-trace
authorJosé Fonseca <jose.r.fonseca@gmail.com>
Thu, 25 Oct 2012 11:28:32 +0000 (12:28 +0100)
committerJosé Fonseca <jose.r.fonseca@gmail.com>
Thu, 25 Oct 2012 11:28:32 +0000 (12:28 +0100)
Conflicts:
retrace/glws_cocoa.mm
wrappers/gltrace_state.cpp

17 files changed:
CMakeLists.txt
common/os_thread.hpp
common/os_workqueue.hpp [new file with mode: 0644]
common/trace_writer_local.cpp
common/workqueue.cpp [new file with mode: 0644]
retrace/glretrace.hpp
retrace/glretrace.py
retrace/glretrace_cgl.cpp
retrace/glretrace_egl.cpp
retrace/glretrace_main.cpp
retrace/glretrace_wgl.cpp
retrace/glretrace_ws.cpp
retrace/glws_cocoa.mm
retrace/glws_glx.cpp
retrace/retrace_main.cpp
specs/gltypes.py
wrappers/gltrace_state.cpp

index 0c020df7a08d51f4a59db101056566dff10c7aa6..f019049e3f561e9f395ee23cfc481621fa443da2 100644 (file)
@@ -1,5 +1,14 @@
 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)
 
 
@@ -25,6 +34,21 @@ set (ENABLE_EGL true CACHE BOOL "Enable EGL support.")
 ##############################################################################
 # 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)
@@ -304,6 +328,7 @@ add_library (common STATIC
     common/image_pnm.cpp
     common/image_png.cpp
     common/${os}
+    common/workqueue.cpp
 )
 
 set_target_properties (common PROPERTIES
index fe7faaa004ae98fc318a07da9ea3aa174ee9fbec..6c0b488c0152474a031a82839b0c767777714fdd 100644 (file)
@@ -1,6 +1,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
@@ -51,7 +72,7 @@ namespace os {
         typedef pthread_mutex_t native_handle_type;
 #endif
 
-        recursive_mutex(void) {
+        _base_mutex(void) {
 #ifdef _WIN32
             InitializeCriticalSection(&_native_handle);
 #else
@@ -63,7 +84,7 @@ namespace os {
 #endif
         }
 
-        ~recursive_mutex() {
+        ~_base_mutex() {
 #ifdef _WIN32
             DeleteCriticalSection(&_native_handle);
 #else
@@ -89,11 +110,150 @@ namespace os {
 #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
     {
@@ -147,17 +307,74 @@ namespace os {
 
         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, 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_ */
diff --git a/common/os_workqueue.hpp b/common/os_workqueue.hpp
new file mode 100644 (file)
index 0000000..047d3db
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef _OS_WORKQUEUE_HPP_
+#define _OS_WORKQUEUE_HPP_
+
+#include <queue>
+
+#include "os_thread.hpp"
+
+namespace os {
+
+
+class WorkQueue;
+
+class WorkQueueWork {
+protected:
+    friend class WorkQueue;
+
+public:
+    virtual void run(void) = 0;
+    virtual ~WorkQueueWork(void) { }
+};
+
+class WorkQueue {
+    std::queue<WorkQueueWork *> work_queue;
+
+    bool busy;
+    bool exit_workqueue;
+    os::condition_variable wake_cond;
+    os::condition_variable complete_cond;
+
+    os::mutex mutex;
+
+    os::thread thread;
+
+    void wake_up_thread(void);
+    int run_tasks(void);
+public:
+    void thread_entry(void);
+    void queue_work(WorkQueueWork *work);
+    void flush(void);
+    void destroy(void);
+
+    WorkQueue(void);
+    ~WorkQueue();
+};
+
+}
+
+#endif
index 5aeedac520febb823ad08e86aa10bd30a05a24ca..feb5c912dfd06b2a61f2a003f9f9759bc23255e0 100644 (file)
@@ -128,8 +128,8 @@ LocalWriter::open(void) {
 #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();
@@ -139,17 +139,13 @@ unsigned LocalWriter::beginEnter(const FunctionSig *sig) {
         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);
 }
 
diff --git a/common/workqueue.cpp b/common/workqueue.cpp
new file mode 100644 (file)
index 0000000..fcd697a
--- /dev/null
@@ -0,0 +1,118 @@
+#include <queue>
+#include <assert.h>
+
+#include "os_workqueue.hpp"
+
+namespace os
+{
+
+/**
+ * return 0 on batch complete, -1 on thread exit request.
+ */
+int WorkQueue::run_tasks(void)
+{
+    os::unique_lock<os::mutex> lock(mutex);
+
+    while (work_queue.empty() && !exit_workqueue) {
+        wake_cond.wait(lock);
+    }
+
+    if (exit_workqueue) {
+        return -1;
+    }
+
+    std::queue<WorkQueueWork *> batch;
+    std::swap(work_queue, batch);
+    busy = true;
+
+    lock.unlock();
+
+    assert(!batch.empty());
+    while (!batch.empty()) {
+        WorkQueueWork *task;
+
+        task = batch.front();
+        task->run();
+        batch.pop();
+        delete task;
+    }
+
+    lock.lock();
+
+    busy = false;
+    complete_cond.signal();
+
+    return 0;
+}
+
+/* Must be called with WorkQueue::lock held */
+void WorkQueue::wake_up_thread(void)
+{
+    wake_cond.signal();
+}
+
+void WorkQueue::queue_work(WorkQueueWork *task)
+{
+    mutex.lock();
+    work_queue.push(task);
+    wake_up_thread();
+    mutex.unlock();
+}
+
+void WorkQueue::flush(void)
+{
+    os::unique_lock<os::mutex> lock(mutex);
+    while (!work_queue.empty() || busy) {
+        complete_cond.wait(lock);
+    }
+}
+
+void WorkQueue::thread_entry(void)
+{
+    int err;
+
+    do {
+        err = run_tasks();
+    } while (!err);
+}
+
+void WorkQueue::destroy(void)
+{
+    mutex.lock();
+    exit_workqueue = true;
+    wake_up_thread();
+    mutex.unlock();
+}
+
+static
+#ifdef _WIN32
+DWORD WINAPI
+#else
+void *
+#endif
+WorkQueue__entry_thunk(void *data)
+{
+    WorkQueue *thread = static_cast<WorkQueue *>(data);
+
+    thread->thread_entry();
+
+#ifdef _WIN32
+    return 0;
+#else
+    return NULL;
+#endif
+}
+
+WorkQueue::WorkQueue(void) :
+    busy(false),
+    exit_workqueue(false),
+    thread(WorkQueue__entry_thunk, this)
+{
+}
+
+WorkQueue::~WorkQueue(void)
+{
+    thread.join();
+}
+
+}
index 6ee241d015e7abb709003f85014606244885800e..95a6d7596df230e8dd059194c493bbf58055cad3 100644 (file)
@@ -41,10 +41,7 @@ struct Context {
     {
     }
 
-    ~Context()
-    {
-        delete wsContext;
-    }
+    ~Context();
 
     glws::Context* wsContext;
 
@@ -65,7 +62,8 @@ extern bool insideList;
 extern bool insideGlBeginEnd;
 
 
-extern Context *currentContext;
+Context *
+getCurrentContext(void);
 
 
 int
index f2cc37232032387af218e300cf6b318eb43e1e3c..e2ea3201bf183bd63597a964b11f95bcc101424f 100644 (file)
@@ -250,7 +250,7 @@ class GlRetracer(Retracer):
             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'    }'
 
@@ -299,8 +299,9 @@ class GlRetracer(Retracer):
         )
 
         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
@@ -362,7 +363,7 @@ class GlRetracer(Retracer):
         # 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;'
@@ -479,7 +480,8 @@ class GlRetracer(Retracer):
             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) {'
index fc834037878b3302c839de0e84af281330db1d41..a0755953b3a52059e35f9d4e0ff9447cba878305 100644 (file)
@@ -239,13 +239,18 @@ static void retrace_CGLFlushDrawable(trace::Call &call) {
     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);
     }
 }
 
@@ -290,7 +295,7 @@ static void retrace_CGLTexImageIOSurface2D(trace::Call &call) {
 
     GLvoid * pixels = NULL;
 
-    if (glretrace::currentContext != context) {
+    if (glretrace::getCurrentContext() != context) {
         if (retrace::debug) {
             retrace::warning(call) << "current context mismatch\n";
         }
index 75688152f80419a8e2ffeb61b3ab2c654e7de865..79b16217b5b51f81bca0ce0f8a799bae02d0c07d 100644 (file)
@@ -127,6 +127,7 @@ static void retrace_eglDestroySurface(trace::Call &call) {
     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;
index f29ad01a07fd07c4d64b204a83897ae95b098bf6..5ccb2e2d2a2955f6b83a03da4508cfbd8ad5d57c 100755 (executable)
@@ -174,12 +174,14 @@ flushQueries() {
 
 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) {
@@ -230,6 +232,8 @@ endProfile(trace::Call &call, bool isDraw) {
 
 void
 initContext() {
+    glretrace::Context *currentContext = glretrace::getCurrentContext();
+
     /* Ensure we have adequate extension support */
     assert(currentContext);
     supportsTimestamp   = currentContext->hasExtension("GL_ARB_timer_query");
@@ -261,6 +265,7 @@ initContext() {
 
     /* Setup debug message call back */
     if (retrace::debug && supportsDebugOutput) {
+        glretrace::Context *currentContext = glretrace::getCurrentContext();
         glDebugMessageCallbackARB(&debugOutputCallback, currentContext);
 
         if (DEBUG_OUTPUT_SYNCHRONOUS) {
@@ -309,10 +314,12 @@ frame_complete(trace::Call &call) {
 
     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";
     }
@@ -403,7 +410,7 @@ retrace::addCallbacks(retrace::Retracer &retracer)
 
 image::Image *
 retrace::getSnapshot(void) {
-    if (!glretrace::currentContext) {
+    if (!glretrace::getCurrentContext()) {
         return NULL;
     }
 
@@ -414,8 +421,10 @@ retrace::getSnapshot(void) {
 bool
 retrace::dumpState(std::ostream &os)
 {
+    glretrace::Context *currentContext = glretrace::getCurrentContext();
+
     if (glretrace::insideGlBeginEnd ||
-        !glretrace::currentContext) {
+        !currentContext) {
         return false;
     }
 
@@ -426,8 +435,11 @@ retrace::dumpState(std::ostream &os)
 
 void
 retrace::flushRendering(void) {
-    glretrace::flushQueries();
-    glFlush();
+    glretrace::Context *currentContext = glretrace::getCurrentContext();
+    if (currentContext) {
+        glretrace::flushQueries();
+        glFlush();
+    }
 }
 
 void
index 8fc9fe667ace5f4cd16f2f8d6b1b4df5cb47fae5..abcf068a057056f88bdefbe1d8c002bd2e9e0459 100644 (file)
@@ -100,8 +100,11 @@ static void retrace_wglSwapBuffers(trace::Call &call) {
     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();
@@ -117,6 +120,7 @@ static void retrace_wglShareLists(trace::Call &call) {
 
     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);
         }
index de9fe08c2c7ed4c471019502eb9dd19a5355ff91..ac03fcc97de6d56390f807a3b1661e6571d489c1 100644 (file)
@@ -31,6 +31,7 @@
 
 #include <string.h>
 
+#include "os_thread.hpp"
 #include "retrace.hpp"
 #include "glproc.hpp"
 #include "glstate.hpp"
@@ -40,9 +41,6 @@
 namespace glretrace {
 
 
-Context *currentContext = NULL;
-
-
 static glws::Visual *
 visuals[glws::PROFILE_MAX];
 
@@ -124,9 +122,23 @@ createContext(Context *shareContext) {
 }
 
 
+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) {
@@ -150,26 +162,25 @@ makeCurrent(trace::Call &call, glws::Drawable *drawable, Context *context)
         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;
+}
 
 
 /**
@@ -180,6 +191,7 @@ makeCurrent(trace::Call &call, glws::Drawable *drawable, Context *context)
  */
 void
 updateDrawable(int width, int height) {
+    Context *currentContext = getCurrentContext();
     if (!currentContext) {
         return;
     }
index 1dd9fee78ea7e0f54550c64a2526814ab62d1bf3..e410862f1996aa278d64db22c2ec3ba4eac81694 100644 (file)
@@ -33,6 +33,8 @@
  * - 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
@@ -159,14 +183,29 @@ public:
 };
 
 
+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];
 }
@@ -180,6 +219,9 @@ cleanup(void) {
 
 Visual *
 createVisual(bool doubleBuffer, Profile profile) {
+
+    initThread();
+
     if (profile != PROFILE_COMPAT &&
         profile != PROFILE_CORE) {
         return nil;
@@ -218,12 +260,16 @@ createVisual(bool doubleBuffer, Profile profile) {
 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;
@@ -248,6 +294,8 @@ createContext(const Visual *visual, Context *shareContext, Profile profile, bool
 bool
 makeCurrent(Drawable *drawable, Context *context)
 {
+    initThread();
+
     if (!drawable || !context) {
         [NSOpenGLContext clearCurrentContext];
     } else {
@@ -266,8 +314,9 @@ makeCurrent(Drawable *drawable, Context *context)
 
 bool
 processEvents(void) {
-   NSEvent* event;
+    initThread();
 
+    NSEvent* event;
     do {
         event = [NSApp nextEventMatchingMask:NSAnyEventMask
                                    untilDate:[NSDate distantPast]
index b6175304d29dd1ff8557027a8dab800d4f3373e0..1494d060d7245cae687637db7c5452cc6a6c2f2b 100644 (file)
@@ -238,6 +238,8 @@ public:
 
 void
 init(void) {
+    XInitThreads();
+
     display = XOpenDisplay(NULL);
     if (!display) {
         std::cerr << "error: unable to open display " << XDisplayName(NULL) << "\n";
index 9386a6182a989621a62a60c5e5b689fbd2fcaac6..9b029c8b5a10e4c0d6c5c9794257b1f74d6b281a 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "os_binary.hpp"
 #include "os_time.hpp"
+#include "os_workqueue.hpp"
 #include "image.hpp"
 #include "trace_callset.hpp"
 #include "trace_dump.hpp"
@@ -36,6 +37,7 @@
 
 
 static bool waitOnFinish = false;
+static bool use_threads;
 
 static const char *comparePrefix = NULL;
 static const char *snapshotPrefix = NULL;
@@ -44,6 +46,8 @@ static trace::CallSet compareFrequency;
 
 static unsigned dumpStateCallNo = ~0;
 
+retrace::Retracer retracer;
+
 
 namespace retrace {
 
@@ -51,6 +55,7 @@ namespace retrace {
 trace::Parser parser;
 trace::Profiler profiler;
 
+static std::map<unsigned long, os::WorkQueue *> thread_wq_map;
 
 int verbosity = 0;
 bool debug = true;
@@ -66,7 +71,22 @@ bool profilingPixelsDrawn = false;
 
 unsigned frameNo = 0;
 unsigned callNo = 0;
+static bool state_dumped;
 
+class RenderWork : public os::WorkQueueWork
+{
+       trace::Call *call;
+public:
+       void run(void);
+       RenderWork(trace::Call *_call) { call = _call; }
+       ~RenderWork(void) { delete call; }
+};
+
+class FlushGLWork : public os::WorkQueueWork
+{
+public:
+    void run(void) { flushRendering(); }
+};
 
 void
 frameComplete(trace::Call &call) {
@@ -119,57 +139,123 @@ takeSnapshot(unsigned call_no) {
     return;
 }
 
+void RenderWork::run(void)
+{
+    bool swapRenderTarget = call->flags &
+        trace::CALL_FLAG_SWAP_RENDERTARGET;
+    bool doSnapshot = snapshotFrequency.contains(*call) ||
+        compareFrequency.contains(*call);
 
-static void
-mainLoop() {
-    retrace::Retracer retracer;
+    if (state_dumped)
+        return;
 
-    addCallbacks(retracer);
+    // 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);
+        }
+    }
 
-    long long startTime = 0; 
-    frameNo = 0;
+    callNo = call->no;
+    retracer.retrace(*call);
 
-    startTime = os::getTime();
-    trace::Call *call;
+    if (doSnapshot && !swapRenderTarget)
+        takeSnapshot(call->no);
 
-    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);
-            }
-        }
+    if (call->no >= dumpStateCallNo && dumpState(std::cout))
+        state_dumped = true;
+}
 
-        callNo = call->no;
-        retracer.retrace(*call);
+static os::WorkQueue *get_work_queue(unsigned long thread_id)
+{
+    os::WorkQueue *thread;
+    std::map<unsigned long, os::WorkQueue *>::iterator it;
 
-        if (doSnapshot && !swapRenderTarget) {
-            takeSnapshot(call->no);
-        }
+    it = thread_wq_map.find(thread_id);
+    if (it == thread_wq_map.end()) {
+        thread = new os::WorkQueue();
+        thread_wq_map[thread_id] = thread;
+    } else {
+        thread = it->second;
+    }
+
+    return thread;
+}
 
-        if (call->no >= dumpStateCallNo &&
-            dumpState(std::cout)) {
-            exit(0);
+static void exit_work_queues(void)
+{
+    std::map<unsigned long, os::WorkQueue *>::iterator it;
+
+    it = thread_wq_map.begin();
+    while (it != thread_wq_map.end()) {
+        os::WorkQueue *thread_wq = it->second;
+
+        thread_wq->queue_work(new FlushGLWork);
+        thread_wq->flush();
+        thread_wq->destroy();
+        thread_wq_map.erase(it++);
+    }
+}
+
+static void do_all_calls(void)
+{
+    trace::Call *call;
+    int prev_thread_id = -1;
+    os::WorkQueue *thread_wq = NULL;
+
+    while ((call = parser.parse_call())) {
+        RenderWork *render_work = new RenderWork(call);
+
+        if (use_threads) {
+            if (prev_thread_id != call->thread_id) {
+                if (thread_wq)
+                    thread_wq->flush();
+                thread_wq = get_work_queue(call->thread_id);
+                prev_thread_id = call->thread_id;
+            }
+
+            thread_wq->queue_work(render_work);
+
+            // XXX: Flush immediately to avoid race conditions on unprotected
+            // static/global variables.
+            thread_wq->flush();
+        } else {
+            render_work->run();
+            delete render_work;
         }
 
-        delete call;
+        if (state_dumped)
+            break;
     }
 
-    // Reached the end of trace
-    flushRendering();
+    exit_work_queues();
+}
+
+
+static void
+mainLoop() {
+    addCallbacks(retracer);
+
+    long long startTime = 0; 
+    frameNo = 0;
+
+    startTime = os::getTime();
+
+    do_all_calls();
+
+    if (!use_threads)
+        /*
+         * Reached the end of trace; if using threads we do the flush
+         * when exiting the threads.
+         */
+        flushRendering();
 
     long long endTime = os::getTime();
     float timeInterval = (endTime - startTime) * (1.0 / os::timeFrequency);
@@ -211,7 +297,8 @@ usage(const char *argv0) {
         "  -S CALLSET   calls to snapshot (default is every frame)\n"
         "  -v           increase output verbosity\n"
         "  -D CALLNO    dump state at specific call no\n"
-        "  -w           waitOnFinish on final frame\n";
+        "  -w           waitOnFinish on final frame\n"
+        "  -t           enable threading\n";
 }
 
 
@@ -289,6 +376,8 @@ int main(int argc, char **argv)
             } else if (!strcmp(arg, "-ppd")) {
                 retrace::profilingPixelsDrawn = true;
             }
+        } else if (!strcmp(arg, "-t")) {
+            use_threads = true;
         } else {
             std::cerr << "error: unknown option " << arg << "\n";
             usage(argv[0]);
index 4324ac7e389ad75a6fb9abfadc5127a685a3a10f..23af3279e54f2e51a33e8ebba9ad6166131a7d36 100644 (file)
@@ -100,7 +100,7 @@ GLshader = Handle("shader", GLuint)
 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)
index 5c7ad1bf66ae242cfe1092ecf8459dcc80814f3a..68bda100354db46025c678618d3d66f2a9d6749e 100644 (file)
@@ -57,15 +57,13 @@ public:
     }
 };
 
-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;