1 /**************************************************************************
3 * Copyright 2011 Jose Fonseca
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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
24 **************************************************************************/
30 #include "os_binary.hpp"
31 #include "os_time.hpp"
33 #include "trace_callset.hpp"
34 #include "trace_dump.hpp"
35 #include "retrace.hpp"
38 static bool waitOnFinish = false;
40 static const char *comparePrefix = NULL;
41 static const char *snapshotPrefix = NULL;
42 static trace::CallSet snapshotFrequency;
43 static trace::CallSet compareFrequency;
45 static unsigned dumpStateCallNo = ~0;
52 trace::Profiler profiler;
57 bool dumpingState = false;
59 bool doubleBuffer = true;
60 bool coreProfile = false;
62 bool profiling = false;
63 bool profilingGpuTimes = false;
64 bool profilingCpuTimes = false;
65 bool profilingPixelsDrawn = false;
72 frameComplete(trace::Call &call) {
78 takeSnapshot(unsigned call_no) {
79 assert(snapshotPrefix || comparePrefix);
81 image::Image *ref = NULL;
84 os::String filename = os::String::format("%s%010u.png", comparePrefix, call_no);
85 ref = image::readPNG(filename);
89 if (retrace::verbosity >= 0) {
90 std::cout << "Read " << filename << "\n";
94 image::Image *src = getSnapshot();
100 if (snapshotPrefix[0] == '-' && snapshotPrefix[1] == 0) {
102 snprintf(comment, sizeof comment, "%u", call_no);
103 src->writePNM(std::cout, comment);
105 os::String filename = os::String::format("%s%010u.png", snapshotPrefix, call_no);
106 if (src->writePNG(filename) && retrace::verbosity >= 0) {
107 std::cout << "Wrote " << filename << "\n";
113 std::cout << "Snapshot " << call_no << " average precision of " << src->compare(*ref) << " bits\n";
125 retrace::Retracer retracer;
127 addCallbacks(retracer);
129 long long startTime = 0;
132 startTime = os::getTime();
135 while ((call = retrace::parser.parse_call())) {
136 bool swapRenderTarget = call->flags & trace::CALL_FLAG_SWAP_RENDERTARGET;
138 snapshotFrequency.contains(*call) ||
139 compareFrequency.contains(*call)
142 // For calls which cause rendertargets to be swaped, we take the
143 // snapshot _before_ swapping the rendertargets.
144 if (doSnapshot && swapRenderTarget) {
145 if (call->flags & trace::CALL_FLAG_END_FRAME) {
146 // For swapbuffers/presents we still use this call number,
147 // spite not have been executed yet.
148 takeSnapshot(call->no);
150 // Whereas for ordinate fbo/rendertarget changes we use the
151 // previous call's number.
152 takeSnapshot(call->no - 1);
157 retracer.retrace(*call);
159 if (doSnapshot && !swapRenderTarget) {
160 takeSnapshot(call->no);
163 if (call->no >= dumpStateCallNo &&
164 dumpState(std::cout)) {
171 // Reached the end of trace
174 long long endTime = os::getTime();
175 float timeInterval = (endTime - startTime) * (1.0 / os::timeFrequency);
177 if ((retrace::verbosity >= -1) || (retrace::profiling)) {
179 "Rendered " << frameNo << " frames"
180 " in " << timeInterval << " secs,"
181 " average of " << (frameNo/timeInterval) << " fps\n";
192 } /* namespace retrace */
196 usage(const char *argv0) {
198 "Usage: " << argv0 << " [OPTION] TRACE [...]\n"
201 " -b benchmark mode (no error checking or warning messages)\n"
202 " -pcpu cpu profiling (cpu times per call)\n"
203 " -pgpu gpu profiling (gpu times per draw call)\n"
204 " -ppd pixels drawn profiling (pixels drawn per draw call)\n"
205 " -c PREFIX compare against snapshots\n"
206 " -C CALLSET calls to compare (default is every frame)\n"
207 " -core use core profile\n"
208 " -db use a double buffer visual (default)\n"
209 " -sb use a single buffer visual\n"
210 " -s PREFIX take snapshots; `-` for PNM stdout output\n"
211 " -S CALLSET calls to snapshot (default is every frame)\n"
212 " -v increase output verbosity\n"
213 " -D CALLNO dump state at specific call no\n"
214 " -w waitOnFinish on final frame\n";
219 int main(int argc, char **argv)
221 using namespace retrace;
223 assert(compareFrequency.empty());
224 assert(snapshotFrequency.empty());
227 for (i = 1; i < argc; ++i) {
228 const char *arg = argv[i];
234 if (!strcmp(arg, "--")) {
236 } else if (!strcmp(arg, "-b")) {
237 retrace::debug = false;
238 retrace::verbosity = -1;
239 } else if (!strcmp(arg, "-c")) {
240 comparePrefix = argv[++i];
241 if (compareFrequency.empty()) {
242 compareFrequency = trace::CallSet(trace::FREQUENCY_FRAME);
244 } else if (!strcmp(arg, "-C")) {
245 compareFrequency = trace::CallSet(argv[++i]);
246 if (comparePrefix == NULL) {
249 } else if (!strcmp(arg, "-D")) {
250 dumpStateCallNo = atoi(argv[++i]);
252 retrace::verbosity = -2;
253 } else if (!strcmp(arg, "-core")) {
254 retrace::coreProfile = true;
255 } else if (!strcmp(arg, "-db")) {
256 retrace::doubleBuffer = true;
257 } else if (!strcmp(arg, "-sb")) {
258 retrace::doubleBuffer = false;
259 } else if (!strcmp(arg, "--help")) {
262 } else if (!strcmp(arg, "-s")) {
263 snapshotPrefix = argv[++i];
264 if (snapshotFrequency.empty()) {
265 snapshotFrequency = trace::CallSet(trace::FREQUENCY_FRAME);
267 if (snapshotPrefix[0] == '-' && snapshotPrefix[1] == 0) {
268 os::setBinaryMode(stdout);
269 retrace::verbosity = -2;
271 } else if (!strcmp(arg, "-S")) {
272 snapshotFrequency = trace::CallSet(argv[++i]);
273 if (snapshotPrefix == NULL) {
276 } else if (!strcmp(arg, "-v")) {
277 ++retrace::verbosity;
278 } else if (!strcmp(arg, "-w")) {
280 } else if (arg[1] == 'p') {
281 retrace::debug = false;
282 retrace::profiling = true;
283 retrace::verbosity = -1;
285 if (!strcmp(arg, "-pcpu")) {
286 retrace::profilingCpuTimes = true;
287 } else if (!strcmp(arg, "-pgpu")) {
288 retrace::profilingGpuTimes = true;
289 } else if (!strcmp(arg, "-ppd")) {
290 retrace::profilingPixelsDrawn = true;
293 std::cerr << "error: unknown option " << arg << "\n";
300 if (retrace::profiling) {
301 retrace::profiler.setup(retrace::profilingCpuTimes, retrace::profilingGpuTimes, retrace::profilingPixelsDrawn);
304 for ( ; i < argc; ++i) {
305 if (!retrace::parser.open(argv[i])) {
306 std::cerr << "error: failed to open " << argv[i] << "\n";
312 retrace::parser.close();
315 // XXX: X often hangs on XCloseDisplay
316 //retrace::cleanUp();