Add support for multi-threaded playback

José Fonseca jose.r.fonseca at gmail.com
Fri Oct 26 10:49:53 PDT 2012


Imre,

Thanks for this. I took your mt-trace patches and did some follow on changes:

- port it to macosx and windows (implement and use C++11-like
threading primitives)

- make threads synchronous i.e., respect the ordering of the calls in
the trace, otherwise there would be random results and race
conditions, as the locking is not captured in the trace.

- make it faster -- the parsing is done in the thread that is
executing, so there is less thread switching.

The code is  in https://github.com/apitrace/apitrace/tree/mt-trace . A
very good sample is android emulator,
http://people.freedesktop.org/~jrfonseca/traces/android-emulator.trace
.

Please give it a try, and let me know it if it still works for you.
In particular let me know if __thread works alright in Android or if
it causes problems.

I hope to merge it into master after a bit more testing and code clean up.

Jose


On Thu, Sep 20, 2012 at 10:33 AM, Imre Deak <imre.deak at intel.com> wrote:
> Signed-off-by: Imre Deak <imre.deak at intel.com>
> ---
>  retrace/retrace_main.cpp |  166 +++++++++++++++++++++++++++++++++++-----------
>  1 file changed, 126 insertions(+), 40 deletions(-)
>
> diff --git a/retrace/retrace_main.cpp b/retrace/retrace_main.cpp
> index 9386a61..df69d4a 100644
> --- a/retrace/retrace_main.cpp
> +++ b/retrace/retrace_main.cpp
> @@ -27,8 +27,10 @@
>  #include <string.h>
>  #include <iostream>
>
> +#include "glws.hpp"
>  #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 +38,7 @@
>
>
>  static bool waitOnFinish = false;
> +static bool use_threads;
>
>  static const char *comparePrefix = NULL;
>  static const char *snapshotPrefix = NULL;
> @@ -44,6 +47,8 @@ static trace::CallSet compareFrequency;
>
>  static unsigned dumpStateCallNo = ~0;
>
> +retrace::Retracer retracer;
> +
>
>  namespace retrace {
>
> @@ -51,6 +56,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 +72,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 +140,119 @@ 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);
> +        } 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 +294,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 +373,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]);
> --
> 1.7.9.5
>


More information about the apitrace mailing list