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"
32 #include "os_workqueue.hpp"
34 #include "trace_callset.hpp"
35 #include "trace_dump.hpp"
36 #include "retrace.hpp"
39 static bool waitOnFinish = false;
40 static bool use_threads;
42 static const char *comparePrefix = NULL;
43 static const char *snapshotPrefix = NULL;
44 static trace::CallSet snapshotFrequency;
45 static trace::CallSet compareFrequency;
47 static unsigned dumpStateCallNo = ~0;
49 retrace::Retracer retracer;
56 trace::Profiler profiler;
58 static std::map<unsigned long, os::WorkQueue *> thread_wq_map;
62 bool dumpingState = false;
64 bool doubleBuffer = true;
65 bool coreProfile = false;
67 bool profiling = false;
68 bool profilingGpuTimes = false;
69 bool profilingCpuTimes = false;
70 bool profilingPixelsDrawn = false;
74 static bool state_dumped;
76 class RenderWork : public os::WorkQueueWork
81 RenderWork(trace::Call *_call) { call = _call; }
82 ~RenderWork(void) { delete call; }
85 class FlushGLWork : public os::WorkQueueWork
88 void run(void) { flushRendering(); }
92 frameComplete(trace::Call &call) {
98 takeSnapshot(unsigned call_no) {
99 assert(snapshotPrefix || comparePrefix);
101 image::Image *ref = NULL;
104 os::String filename = os::String::format("%s%010u.png", comparePrefix, call_no);
105 ref = image::readPNG(filename);
109 if (retrace::verbosity >= 0) {
110 std::cout << "Read " << filename << "\n";
114 image::Image *src = getSnapshot();
119 if (snapshotPrefix) {
120 if (snapshotPrefix[0] == '-' && snapshotPrefix[1] == 0) {
122 snprintf(comment, sizeof comment, "%u", call_no);
123 src->writePNM(std::cout, comment);
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";
133 std::cout << "Snapshot " << call_no << " average precision of " << src->compare(*ref) << " bits\n";
142 void RenderWork::run(void)
144 bool swapRenderTarget = call->flags &
145 trace::CALL_FLAG_SWAP_RENDERTARGET;
146 bool doSnapshot = snapshotFrequency.contains(*call) ||
147 compareFrequency.contains(*call);
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);
160 // Whereas for ordinate fbo/rendertarget changes we
161 // use the previous call's number.
162 takeSnapshot(call->no - 1);
167 retracer.retrace(*call);
169 if (doSnapshot && !swapRenderTarget)
170 takeSnapshot(call->no);
172 if (call->no >= dumpStateCallNo && dumpState(std::cout))
176 static os::WorkQueue *get_work_queue(unsigned long thread_id)
178 os::WorkQueue *thread;
179 std::map<unsigned long, os::WorkQueue *>::iterator it;
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;
192 static void exit_work_queues(void)
194 std::map<unsigned long, os::WorkQueue *>::iterator it;
196 it = thread_wq_map.begin();
197 while (it != thread_wq_map.end()) {
198 os::WorkQueue *thread_wq = it->second;
200 thread_wq->queue_work(new FlushGLWork);
202 thread_wq->destroy();
203 thread_wq_map.erase(it++);
207 static void do_all_calls(void)
210 int prev_thread_id = -1;
211 os::WorkQueue *thread_wq = NULL;
213 while ((call = parser.parse_call())) {
214 RenderWork *render_work = new RenderWork(call);
217 if (prev_thread_id != call->thread_id) {
220 thread_wq = get_work_queue(call->thread_id);
221 prev_thread_id = call->thread_id;
224 thread_wq->queue_work(render_work);
226 // XXX: Flush immediately to avoid race conditions on unprotected
227 // static/global variables.
244 addCallbacks(retracer);
246 long long startTime = 0;
249 startTime = os::getTime();
255 * Reached the end of trace; if using threads we do the flush
256 * when exiting the threads.
260 long long endTime = os::getTime();
261 float timeInterval = (endTime - startTime) * (1.0 / os::timeFrequency);
263 if ((retrace::verbosity >= -1) || (retrace::profiling)) {
265 "Rendered " << frameNo << " frames"
266 " in " << timeInterval << " secs,"
267 " average of " << (frameNo/timeInterval) << " fps\n";
278 } /* namespace retrace */
282 usage(const char *argv0) {
284 "Usage: " << argv0 << " [OPTION] TRACE [...]\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";
306 int main(int argc, char **argv)
308 using namespace retrace;
310 assert(compareFrequency.empty());
311 assert(snapshotFrequency.empty());
314 for (i = 1; i < argc; ++i) {
315 const char *arg = argv[i];
321 if (!strcmp(arg, "--")) {
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);
331 } else if (!strcmp(arg, "-C")) {
332 compareFrequency = trace::CallSet(argv[++i]);
333 if (comparePrefix == NULL) {
336 } else if (!strcmp(arg, "-D")) {
337 dumpStateCallNo = atoi(argv[++i]);
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")) {
349 } else if (!strcmp(arg, "-s")) {
350 snapshotPrefix = argv[++i];
351 if (snapshotFrequency.empty()) {
352 snapshotFrequency = trace::CallSet(trace::FREQUENCY_FRAME);
354 if (snapshotPrefix[0] == '-' && snapshotPrefix[1] == 0) {
355 os::setBinaryMode(stdout);
356 retrace::verbosity = -2;
358 } else if (!strcmp(arg, "-S")) {
359 snapshotFrequency = trace::CallSet(argv[++i]);
360 if (snapshotPrefix == NULL) {
363 } else if (!strcmp(arg, "-v")) {
364 ++retrace::verbosity;
365 } else if (!strcmp(arg, "-w")) {
367 } else if (arg[1] == 'p') {
368 retrace::debug = false;
369 retrace::profiling = true;
370 retrace::verbosity = -1;
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;
379 } else if (!strcmp(arg, "-t")) {
382 std::cerr << "error: unknown option " << arg << "\n";
389 if (retrace::profiling) {
390 retrace::profiler.setup(retrace::profilingCpuTimes, retrace::profilingGpuTimes, retrace::profilingPixelsDrawn);
393 for ( ; i < argc; ++i) {
394 if (!retrace::parser.open(argv[i])) {
395 std::cerr << "error: failed to open " << argv[i] << "\n";
401 retrace::parser.close();
404 // XXX: X often hangs on XCloseDisplay
405 //retrace::cleanUp();