]> git.cworth.org Git - apitrace/blob - wrappers/gltrace_state.cpp
mt trace: add helper to track GL contexts on a per-thread basis
[apitrace] / wrappers / gltrace_state.cpp
1 #include <gltrace.hpp>
2 #include <os_thread.hpp>
3 #include <assert.h>
4 #include <tr1/memory>
5
6 namespace gltrace {
7
8 typedef std::tr1::shared_ptr<Context> context_ptr_t;
9 static std::map<uintptr_t, context_ptr_t> context_map;
10 static os::recursive_mutex context_map_mutex;
11
12 class ThreadState {
13 public:
14     context_ptr_t current_context;
15     context_ptr_t dummy_context;     /*
16                                       * For cases when there is no current
17                                       * context, but the app still calls some
18                                       * GL function that expects one.
19                                       */
20     ThreadState() : dummy_context(new Context)
21     {
22         current_context = dummy_context;
23     }
24 };
25
26 static os::thread_specific_ptr<struct ThreadState> thread_state;
27
28 static ThreadState *get_ts(void)
29 {
30     ThreadState *ts = thread_state.get();
31
32     if (!ts) {
33         ts = new ThreadState;
34         thread_state.reset(ts);
35     }
36
37     return ts;
38 }
39
40 static void _retainContext(context_ptr_t ctx)
41 {
42     ctx->retain_count++;
43 }
44
45 void retainContext(uintptr_t context_id)
46 {
47     context_map_mutex.lock();
48     if (context_map.find(context_id) != context_map.end())
49         _retainContext(context_map[context_id]);
50     context_map_mutex.unlock();
51 }
52
53 static bool _releaseContext(context_ptr_t ctx)
54 {
55     return !(--ctx->retain_count);
56 }
57
58 /*
59  * return true if the context was destroyed, false if only its refcount
60  * got decreased. Note that even if the context was destroyed it may
61  * still live, if it's the currently selected context (by setContext).
62  */
63 bool releaseContext(uintptr_t context_id)
64 {
65     bool res;
66
67     context_map_mutex.lock();
68     /*
69      * This can potentially called (from glX) with an invalid context_id,
70      * so don't assert on it being valid.
71      */
72     if (context_map.find(context_id) != context_map.end()) {
73         res = _releaseContext(context_map[context_id]);
74         if (res)
75             context_map.erase(context_id);
76     }
77     context_map_mutex.unlock();
78
79     return res;
80 }
81
82 void createContext(uintptr_t context_id)
83 {
84     context_ptr_t ctx(new Context);
85
86     context_map_mutex.lock();
87
88     _retainContext(ctx);
89     assert(context_map.find(context_id) == context_map.end());
90     context_map[context_id] = ctx;
91
92     context_map_mutex.unlock();
93 }
94
95 /*
96  * return true if the context has been destroyed, false otherwise. See
97  * the note at releaseContext about the actual ccontext lifetime.
98  */
99 bool destroyContext(uintptr_t context_id)
100 {
101     return releaseContext(context_id);
102 }
103
104 void setContext(uintptr_t context_id)
105 {
106     ThreadState *ts = get_ts();
107     context_ptr_t ctx;
108
109     context_map_mutex.lock();
110
111     assert(context_map.find(context_id) != context_map.end());
112     ctx = context_map[context_id];
113
114     context_map_mutex.unlock();
115
116     ts->current_context = ctx;
117 }
118
119 void clearContext(void)
120 {
121     ThreadState *ts = get_ts();
122
123     ts->current_context = ts->dummy_context;
124 }
125
126 Context *getContext(void)
127 {
128     return get_ts()->current_context.get();
129 }
130
131 }