]> git.cworth.org Git - apitrace/blob - retrace/retrace_main.cpp
Improved profiling capabilities.
[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 "image.hpp"
33 #include "trace_callset.hpp"
34 #include "trace_dump.hpp"
35 #include "retrace.hpp"
36
37
38 static bool waitOnFinish = false;
39
40 static const char *comparePrefix = NULL;
41 static const char *snapshotPrefix = NULL;
42 static trace::CallSet snapshotFrequency;
43 static trace::CallSet compareFrequency;
44
45 static unsigned dumpStateCallNo = ~0;
46
47
48 namespace retrace {
49
50
51 trace::Parser parser;
52 trace::Profiler profiler;
53
54
55 int verbosity = 0;
56 bool debug = true;
57 bool dumpingState = false;
58
59 bool doubleBuffer = true;
60 bool coreProfile = false;
61
62 bool profiling = false;
63 bool profilingGpuTimes = false;
64 bool profilingCpuTimes = false;
65 bool profilingPixelsDrawn = false;
66
67 unsigned frameNo = 0;
68
69
70 void
71 frameComplete(trace::Call &call) {
72     ++frameNo;
73 }
74
75
76 static void
77 takeSnapshot(unsigned call_no) {
78     assert(snapshotPrefix || comparePrefix);
79
80     image::Image *ref = NULL;
81
82     if (comparePrefix) {
83         os::String filename = os::String::format("%s%010u.png", comparePrefix, call_no);
84         ref = image::readPNG(filename);
85         if (!ref) {
86             return;
87         }
88         if (retrace::verbosity >= 0) {
89             std::cout << "Read " << filename << "\n";
90         }
91     }
92
93     image::Image *src = getSnapshot();
94     if (!src) {
95         return;
96     }
97
98     if (snapshotPrefix) {
99         if (snapshotPrefix[0] == '-' && snapshotPrefix[1] == 0) {
100             char comment[21];
101             snprintf(comment, sizeof comment, "%u", call_no);
102             src->writePNM(std::cout, comment);
103         } else {
104             os::String filename = os::String::format("%s%010u.png", snapshotPrefix, call_no);
105             if (src->writePNG(filename) && retrace::verbosity >= 0) {
106                 std::cout << "Wrote " << filename << "\n";
107             }
108         }
109     }
110
111     if (ref) {
112         std::cout << "Snapshot " << call_no << " average precision of " << src->compare(*ref) << " bits\n";
113         delete ref;
114     }
115
116     delete src;
117
118     return;
119 }
120
121
122 static void
123 mainLoop() {
124     retrace::Retracer retracer;
125
126     addCallbacks(retracer);
127
128     long long startTime = 0; 
129     frameNo = 0;
130
131     startTime = os::getTime();
132     trace::Call *call;
133
134     while ((call = retrace::parser.parse_call())) {
135         bool swapRenderTarget = call->flags & trace::CALL_FLAG_SWAP_RENDERTARGET;
136         bool doSnapshot =
137             snapshotFrequency.contains(*call) ||
138             compareFrequency.contains(*call)
139         ;
140
141         // For calls which cause rendertargets to be swaped, we take the
142         // snapshot _before_ swapping the rendertargets.
143         if (doSnapshot && swapRenderTarget) {
144             if (call->flags & trace::CALL_FLAG_END_FRAME) {
145                 // For swapbuffers/presents we still use this call number,
146                 // spite not have been executed yet.
147                 takeSnapshot(call->no);
148             } else {
149                 // Whereas for ordinate fbo/rendertarget changes we use the
150                 // previous call's number.
151                 takeSnapshot(call->no - 1);
152             }
153         }
154
155         retracer.retrace(*call);
156
157         if (doSnapshot && !swapRenderTarget) {
158             takeSnapshot(call->no);
159         }
160
161         if (call->no >= dumpStateCallNo &&
162             dumpState(std::cout)) {
163             exit(0);
164         }
165
166         delete call;
167     }
168
169     // Reached the end of trace
170     flushRendering();
171
172     long long endTime = os::getTime();
173     float timeInterval = (endTime - startTime) * (1.0 / os::timeFrequency);
174
175     if ((retrace::verbosity >= -1) || (retrace::profiling)) {
176         std::cout << 
177             "Rendered " << frameNo << " frames"
178             " in " <<  timeInterval << " secs,"
179             " average of " << (frameNo/timeInterval) << " fps\n";
180     }
181
182     if (waitOnFinish) {
183         waitForInput();
184     } else {
185         return;
186     }
187 }
188
189
190 } /* namespace retrace */
191
192
193 static void
194 usage(const char *argv0) {
195     std::cout << 
196         "Usage: " << argv0 << " [OPTION] TRACE [...]\n"
197         "Replay TRACE.\n"
198         "\n"
199         "  -b           benchmark mode (no error checking or warning messages)\n"
200         "  -pcpu        cpu profiling (cpu times per call)\n"
201         "  -pgpu        gpu profiling (gpu times per draw call)\n"
202         "  -ppd         pixels drawn profiling (pixels drawn per draw call)\n"
203         "  -c PREFIX    compare against snapshots\n"
204         "  -C CALLSET   calls to compare (default is every frame)\n"
205         "  -core        use core profile\n"
206         "  -db          use a double buffer visual (default)\n"
207         "  -sb          use a single buffer visual\n"
208         "  -s PREFIX    take snapshots; `-` for PNM stdout output\n"
209         "  -S CALLSET   calls to snapshot (default is every frame)\n"
210         "  -v           increase output verbosity\n"
211         "  -D CALLNO    dump state at specific call no\n"
212         "  -w           waitOnFinish on final frame\n";
213 }
214
215
216 extern "C"
217 int main(int argc, char **argv)
218 {
219     using namespace retrace;
220
221     assert(compareFrequency.empty());
222     assert(snapshotFrequency.empty());
223
224     int i;
225     for (i = 1; i < argc; ++i) {
226         const char *arg = argv[i];
227
228         if (arg[0] != '-') {
229             break;
230         }
231
232         if (!strcmp(arg, "--")) {
233             break;
234         } else if (!strcmp(arg, "-b")) {
235             retrace::debug = false;
236             retrace::verbosity = -1;
237         } else if (!strcmp(arg, "-c")) {
238             comparePrefix = argv[++i];
239             if (compareFrequency.empty()) {
240                 compareFrequency = trace::CallSet(trace::FREQUENCY_FRAME);
241             }
242         } else if (!strcmp(arg, "-C")) {
243             compareFrequency = trace::CallSet(argv[++i]);
244             if (comparePrefix == NULL) {
245                 comparePrefix = "";
246             }
247         } else if (!strcmp(arg, "-D")) {
248             dumpStateCallNo = atoi(argv[++i]);
249             dumpingState = true;
250             retrace::verbosity = -2;
251         } else if (!strcmp(arg, "-core")) {
252             retrace::coreProfile = true;
253         } else if (!strcmp(arg, "-db")) {
254             retrace::doubleBuffer = true;
255         } else if (!strcmp(arg, "-sb")) {
256             retrace::doubleBuffer = false;
257         } else if (!strcmp(arg, "--help")) {
258             usage(argv[0]);
259             return 0;
260         } else if (!strcmp(arg, "-s")) {
261             snapshotPrefix = argv[++i];
262             if (snapshotFrequency.empty()) {
263                 snapshotFrequency = trace::CallSet(trace::FREQUENCY_FRAME);
264             }
265             if (snapshotPrefix[0] == '-' && snapshotPrefix[1] == 0) {
266                 os::setBinaryMode(stdout);
267                 retrace::verbosity = -2;
268             }
269         } else if (!strcmp(arg, "-S")) {
270             snapshotFrequency = trace::CallSet(argv[++i]);
271             if (snapshotPrefix == NULL) {
272                 snapshotPrefix = "";
273             }
274         } else if (!strcmp(arg, "-v")) {
275             ++retrace::verbosity;
276         } else if (!strcmp(arg, "-w")) {
277             waitOnFinish = true;
278         } else if (arg[1] == 'p') {
279             retrace::debug = false;
280             retrace::profiling = true;
281             retrace::verbosity = -1;
282
283             if (!strcmp(arg, "-pcpu")) {
284                 retrace::profilingCpuTimes = true;
285             } else if (!strcmp(arg, "-pgpu")) {
286                 retrace::profilingGpuTimes = true;
287             } else if (!strcmp(arg, "-ppd")) {
288                 retrace::profilingPixelsDrawn = true;
289             }
290         } else {
291             std::cerr << "error: unknown option " << arg << "\n";
292             usage(argv[0]);
293             return 1;
294         }
295     }
296
297     retrace::setUp();
298     if (retrace::profiling) {
299         retrace::profiler.setup(retrace::profilingCpuTimes, retrace::profilingGpuTimes, retrace::profilingPixelsDrawn);
300     }
301
302     for ( ; i < argc; ++i) {
303         if (!retrace::parser.open(argv[i])) {
304             std::cerr << "error: failed to open " << argv[i] << "\n";
305             return 1;
306         }
307
308         retrace::mainLoop();
309
310         retrace::parser.close();
311     }
312
313     // XXX: X often hangs on XCloseDisplay
314     //retrace::cleanUp();
315
316     return 0;
317 }
318