]> git.cworth.org Git - apitrace/blob - retrace/retrace_main.cpp
Merge remote-tracking branch 'github/master' into mt-trace
[apitrace] / retrace / retrace_main.cpp
1 /**************************************************************************
2  *
3  * Copyright 2011 Jose Fonseca
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25
26
27 #include <string.h>
28 #include <iostream>
29
30 #include "os_binary.hpp"
31 #include "os_time.hpp"
32 #include "os_workqueue.hpp"
33 #include "image.hpp"
34 #include "trace_callset.hpp"
35 #include "trace_dump.hpp"
36 #include "retrace.hpp"
37
38
39 static bool waitOnFinish = false;
40 static bool use_threads;
41
42 static const char *comparePrefix = NULL;
43 static const char *snapshotPrefix = NULL;
44 static trace::CallSet snapshotFrequency;
45 static trace::CallSet compareFrequency;
46
47 static unsigned dumpStateCallNo = ~0;
48
49 retrace::Retracer retracer;
50
51
52 namespace retrace {
53
54
55 trace::Parser parser;
56 trace::Profiler profiler;
57
58 static std::map<unsigned long, os::WorkQueue *> thread_wq_map;
59
60 int verbosity = 0;
61 bool debug = true;
62 bool dumpingState = false;
63
64 bool doubleBuffer = true;
65 bool coreProfile = false;
66
67 bool profiling = false;
68 bool profilingGpuTimes = false;
69 bool profilingCpuTimes = false;
70 bool profilingPixelsDrawn = false;
71
72 unsigned frameNo = 0;
73 unsigned callNo = 0;
74 static bool state_dumped;
75
76 class RenderWork : public os::WorkQueueWork
77 {
78         trace::Call *call;
79 public:
80         void run(void);
81         RenderWork(trace::Call *_call) { call = _call; }
82         ~RenderWork(void) { delete call; }
83 };
84
85 class FlushGLWork : public os::WorkQueueWork
86 {
87 public:
88     void run(void) { flushRendering(); }
89 };
90
91 void
92 frameComplete(trace::Call &call) {
93     ++frameNo;
94 }
95
96
97 static void
98 takeSnapshot(unsigned call_no) {
99     assert(snapshotPrefix || comparePrefix);
100
101     image::Image *ref = NULL;
102
103     if (comparePrefix) {
104         os::String filename = os::String::format("%s%010u.png", comparePrefix, call_no);
105         ref = image::readPNG(filename);
106         if (!ref) {
107             return;
108         }
109         if (retrace::verbosity >= 0) {
110             std::cout << "Read " << filename << "\n";
111         }
112     }
113
114     image::Image *src = getSnapshot();
115     if (!src) {
116         return;
117     }
118
119     if (snapshotPrefix) {
120         if (snapshotPrefix[0] == '-' && snapshotPrefix[1] == 0) {
121             char comment[21];
122             snprintf(comment, sizeof comment, "%u", call_no);
123             src->writePNM(std::cout, comment);
124         } else {
125             os::String filename = os::String::format("%s%010u.png", snapshotPrefix, call_no);
126             if (src->writePNG(filename) && retrace::verbosity >= 0) {
127                 std::cout << "Wrote " << filename << "\n";
128             }
129         }
130     }
131
132     if (ref) {
133         std::cout << "Snapshot " << call_no << " average precision of " << src->compare(*ref) << " bits\n";
134         delete ref;
135     }
136
137     delete src;
138
139     return;
140 }
141
142 void RenderWork::run(void)
143 {
144     bool swapRenderTarget = call->flags &
145         trace::CALL_FLAG_SWAP_RENDERTARGET;
146     bool doSnapshot = snapshotFrequency.contains(*call) ||
147         compareFrequency.contains(*call);
148
149     if (state_dumped)
150         return;
151
152     // For calls which cause rendertargets to be swaped, we take the
153     // snapshot _before_ swapping the rendertargets.
154     if (doSnapshot && swapRenderTarget) {
155         if (call->flags & trace::CALL_FLAG_END_FRAME) {
156             // For swapbuffers/presents we still use this
157             // call number, spite not have been executed yet.
158             takeSnapshot(call->no);
159         } else {
160             // Whereas for ordinate fbo/rendertarget changes we
161             // use the previous call's number.
162             takeSnapshot(call->no - 1);
163         }
164     }
165
166     callNo = call->no;
167     retracer.retrace(*call);
168
169     if (doSnapshot && !swapRenderTarget)
170         takeSnapshot(call->no);
171
172     if (call->no >= dumpStateCallNo && dumpState(std::cout))
173         state_dumped = true;
174 }
175
176 static os::WorkQueue *get_work_queue(unsigned long thread_id)
177 {
178     os::WorkQueue *thread;
179     std::map<unsigned long, os::WorkQueue *>::iterator it;
180
181     it = thread_wq_map.find(thread_id);
182     if (it == thread_wq_map.end()) {
183         thread = new os::WorkQueue();
184         thread_wq_map[thread_id] = thread;
185     } else {
186         thread = it->second;
187     }
188
189     return thread;
190 }
191
192 static void exit_work_queues(void)
193 {
194     std::map<unsigned long, os::WorkQueue *>::iterator it;
195
196     it = thread_wq_map.begin();
197     while (it != thread_wq_map.end()) {
198         os::WorkQueue *thread_wq = it->second;
199
200         thread_wq->queue_work(new FlushGLWork);
201         thread_wq->flush();
202         thread_wq->destroy();
203         thread_wq_map.erase(it++);
204     }
205 }
206
207 static void do_all_calls(void)
208 {
209     trace::Call *call;
210     int prev_thread_id = -1;
211     os::WorkQueue *thread_wq = NULL;
212
213     while ((call = parser.parse_call())) {
214         RenderWork *render_work = new RenderWork(call);
215
216         if (use_threads) {
217             if (prev_thread_id != call->thread_id) {
218                 if (thread_wq)
219                     thread_wq->flush();
220                 thread_wq = get_work_queue(call->thread_id);
221                 prev_thread_id = call->thread_id;
222             }
223
224             thread_wq->queue_work(render_work);
225
226             // XXX: Flush immediately to avoid race conditions on unprotected
227             // static/global variables.
228             thread_wq->flush();
229         } else {
230             render_work->run();
231             delete render_work;
232         }
233
234         if (state_dumped)
235             break;
236     }
237
238     exit_work_queues();
239 }
240
241
242 static void
243 mainLoop() {
244     addCallbacks(retracer);
245
246     long long startTime = 0; 
247     frameNo = 0;
248
249     startTime = os::getTime();
250
251     do_all_calls();
252
253     if (!use_threads)
254         /*
255          * Reached the end of trace; if using threads we do the flush
256          * when exiting the threads.
257          */
258         flushRendering();
259
260     long long endTime = os::getTime();
261     float timeInterval = (endTime - startTime) * (1.0 / os::timeFrequency);
262
263     if ((retrace::verbosity >= -1) || (retrace::profiling)) {
264         std::cout << 
265             "Rendered " << frameNo << " frames"
266             " in " <<  timeInterval << " secs,"
267             " average of " << (frameNo/timeInterval) << " fps\n";
268     }
269
270     if (waitOnFinish) {
271         waitForInput();
272     } else {
273         return;
274     }
275 }
276
277
278 } /* namespace retrace */
279
280
281 static void
282 usage(const char *argv0) {
283     std::cout << 
284         "Usage: " << argv0 << " [OPTION] TRACE [...]\n"
285         "Replay TRACE.\n"
286         "\n"
287         "  -b           benchmark mode (no error checking or warning messages)\n"
288         "  -pcpu        cpu profiling (cpu times per call)\n"
289         "  -pgpu        gpu profiling (gpu times per draw call)\n"
290         "  -ppd         pixels drawn profiling (pixels drawn per draw call)\n"
291         "  -c PREFIX    compare against snapshots\n"
292         "  -C CALLSET   calls to compare (default is every frame)\n"
293         "  -core        use core profile\n"
294         "  -db          use a double buffer visual (default)\n"
295         "  -sb          use a single buffer visual\n"
296         "  -s PREFIX    take snapshots; `-` for PNM stdout output\n"
297         "  -S CALLSET   calls to snapshot (default is every frame)\n"
298         "  -v           increase output verbosity\n"
299         "  -D CALLNO    dump state at specific call no\n"
300         "  -w           waitOnFinish on final frame\n"
301         "  -t           enable threading\n";
302 }
303
304
305 extern "C"
306 int main(int argc, char **argv)
307 {
308     using namespace retrace;
309
310     assert(compareFrequency.empty());
311     assert(snapshotFrequency.empty());
312
313     int i;
314     for (i = 1; i < argc; ++i) {
315         const char *arg = argv[i];
316
317         if (arg[0] != '-') {
318             break;
319         }
320
321         if (!strcmp(arg, "--")) {
322             break;
323         } else if (!strcmp(arg, "-b")) {
324             retrace::debug = false;
325             retrace::verbosity = -1;
326         } else if (!strcmp(arg, "-c")) {
327             comparePrefix = argv[++i];
328             if (compareFrequency.empty()) {
329                 compareFrequency = trace::CallSet(trace::FREQUENCY_FRAME);
330             }
331         } else if (!strcmp(arg, "-C")) {
332             compareFrequency = trace::CallSet(argv[++i]);
333             if (comparePrefix == NULL) {
334                 comparePrefix = "";
335             }
336         } else if (!strcmp(arg, "-D")) {
337             dumpStateCallNo = atoi(argv[++i]);
338             dumpingState = true;
339             retrace::verbosity = -2;
340         } else if (!strcmp(arg, "-core")) {
341             retrace::coreProfile = true;
342         } else if (!strcmp(arg, "-db")) {
343             retrace::doubleBuffer = true;
344         } else if (!strcmp(arg, "-sb")) {
345             retrace::doubleBuffer = false;
346         } else if (!strcmp(arg, "--help")) {
347             usage(argv[0]);
348             return 0;
349         } else if (!strcmp(arg, "-s")) {
350             snapshotPrefix = argv[++i];
351             if (snapshotFrequency.empty()) {
352                 snapshotFrequency = trace::CallSet(trace::FREQUENCY_FRAME);
353             }
354             if (snapshotPrefix[0] == '-' && snapshotPrefix[1] == 0) {
355                 os::setBinaryMode(stdout);
356                 retrace::verbosity = -2;
357             }
358         } else if (!strcmp(arg, "-S")) {
359             snapshotFrequency = trace::CallSet(argv[++i]);
360             if (snapshotPrefix == NULL) {
361                 snapshotPrefix = "";
362             }
363         } else if (!strcmp(arg, "-v")) {
364             ++retrace::verbosity;
365         } else if (!strcmp(arg, "-w")) {
366             waitOnFinish = true;
367         } else if (arg[1] == 'p') {
368             retrace::debug = false;
369             retrace::profiling = true;
370             retrace::verbosity = -1;
371
372             if (!strcmp(arg, "-pcpu")) {
373                 retrace::profilingCpuTimes = true;
374             } else if (!strcmp(arg, "-pgpu")) {
375                 retrace::profilingGpuTimes = true;
376             } else if (!strcmp(arg, "-ppd")) {
377                 retrace::profilingPixelsDrawn = true;
378             }
379         } else if (!strcmp(arg, "-t")) {
380             use_threads = true;
381         } else {
382             std::cerr << "error: unknown option " << arg << "\n";
383             usage(argv[0]);
384             return 1;
385         }
386     }
387
388     retrace::setUp();
389     if (retrace::profiling) {
390         retrace::profiler.setup(retrace::profilingCpuTimes, retrace::profilingGpuTimes, retrace::profilingPixelsDrawn);
391     }
392
393     for ( ; i < argc; ++i) {
394         if (!retrace::parser.open(argv[i])) {
395             std::cerr << "error: failed to open " << argv[i] << "\n";
396             return 1;
397         }
398
399         retrace::mainLoop();
400
401         retrace::parser.close();
402     }
403
404     // XXX: X often hangs on XCloseDisplay
405     //retrace::cleanUp();
406
407     return 0;
408 }
409