64 int currentFrame; |
64 int currentFrame; |
65 int currentIteration; |
65 int currentIteration; |
66 QTime timer; |
66 QTime timer; |
67 |
67 |
68 QList<uint> visibleUpdates; |
68 QList<uint> visibleUpdates; |
69 QList<uint> iterationTimes; |
69 |
|
70 QVector<uint> iterationTimes; |
70 QString filename; |
71 QString filename; |
|
72 |
|
73 int from; |
|
74 int to; |
|
75 |
|
76 bool single; |
|
77 |
|
78 int frame; |
|
79 int currentCommand; |
71 }; |
80 }; |
72 |
81 |
73 void ReplayWidget::updateRect() |
82 void ReplayWidget::updateRect() |
74 { |
83 { |
75 if (!visibleUpdates.isEmpty()) |
84 if (frame >= 0 && !updates.isEmpty()) |
|
85 update(updates.at(frame)); |
|
86 else if (!visibleUpdates.isEmpty()) |
76 update(updates.at(visibleUpdates.at(currentFrame))); |
87 update(updates.at(visibleUpdates.at(currentFrame))); |
77 } |
88 } |
78 |
89 |
|
90 const int singleFrameRepeatsPerCommand = 100; |
|
91 const int singleFrameIterations = 4; |
|
92 |
79 void ReplayWidget::paintEvent(QPaintEvent *) |
93 void ReplayWidget::paintEvent(QPaintEvent *) |
80 { |
94 { |
81 QPainter p(this); |
95 QPainter p(this); |
82 |
96 |
|
97 QTimer::singleShot(0, this, SLOT(updateRect())); |
|
98 |
83 // p.setClipRegion(frames.at(currentFrame).updateRegion); |
99 // p.setClipRegion(frames.at(currentFrame).updateRegion); |
|
100 |
|
101 if (frame >= 0) { |
|
102 int start = buffer.frameStartIndex(frame); |
|
103 int end = buffer.frameEndIndex(frame); |
|
104 |
|
105 iterationTimes.resize(end - start); |
|
106 |
|
107 int saveRestoreStackDepth = buffer.processCommands(&p, start, start + currentCommand); |
|
108 |
|
109 for (int i = 0; i < saveRestoreStackDepth; ++i) |
|
110 p.restore(); |
|
111 |
|
112 const int repeats = currentIteration >= 3 ? singleFrameRepeatsPerCommand : 1; |
|
113 |
|
114 ++currentFrame; |
|
115 if (currentFrame == repeats) { |
|
116 currentFrame = 0; |
|
117 if (currentIteration >= 3) { |
|
118 iterationTimes[currentCommand - 1] = qMin(iterationTimes[currentCommand - 1], uint(timer.elapsed())); |
|
119 timer.restart(); |
|
120 } |
|
121 |
|
122 if (currentIteration >= singleFrameIterations + 3) { |
|
123 printf(" # | ms | description\n"); |
|
124 printf("------+---------+------------------------------------------------------------\n"); |
|
125 |
|
126 qSort(iterationTimes); |
|
127 |
|
128 int sum = 0; |
|
129 for (int i = 0; i < iterationTimes.size(); ++i) { |
|
130 int delta = iterationTimes.at(i); |
|
131 if (i > 0) |
|
132 delta -= iterationTimes.at(i-1); |
|
133 sum += delta; |
|
134 qreal deltaF = delta / qreal(repeats); |
|
135 printf("%.5d | %.5f | %s\n", i, deltaF, qPrintable(buffer.commandDescription(start + i))); |
|
136 } |
|
137 printf("Total | %.5f | Total frame time\n", sum / qreal(repeats)); |
|
138 deleteLater(); |
|
139 return; |
|
140 } |
|
141 |
|
142 if (start + currentCommand >= end) { |
|
143 currentCommand = 1; |
|
144 ++currentIteration; |
|
145 if (currentIteration == 3) { |
|
146 timer.start(); |
|
147 iterationTimes.fill(uint(-1)); |
|
148 } |
|
149 if (currentIteration >= 3 && currentIteration < singleFrameIterations + 3) |
|
150 printf("Profiling iteration %d of %d\n", currentIteration - 2, singleFrameIterations); |
|
151 } else { |
|
152 ++currentCommand; |
|
153 } |
|
154 } |
|
155 |
|
156 return; |
|
157 } |
84 |
158 |
85 buffer.draw(&p, visibleUpdates.at(currentFrame)); |
159 buffer.draw(&p, visibleUpdates.at(currentFrame)); |
86 |
160 |
87 ++currentFrame; |
161 ++currentFrame; |
88 if (currentFrame >= visibleUpdates.size()) { |
162 if (currentFrame >= visibleUpdates.size()) { |
89 currentFrame = 0; |
163 currentFrame = 0; |
90 ++currentIteration; |
164 ++currentIteration; |
|
165 |
|
166 if (single) { |
|
167 deleteLater(); |
|
168 return; |
|
169 } |
91 |
170 |
92 if (currentIteration == 3) |
171 if (currentIteration == 3) |
93 timer.start(); |
172 timer.start(); |
94 else if (currentIteration > 3) { |
173 else if (currentIteration > 3) { |
95 iterationTimes << timer.elapsed(); |
174 iterationTimes << timer.elapsed(); |
126 return; |
205 return; |
127 } |
206 } |
128 } |
207 } |
129 } |
208 } |
130 } |
209 } |
131 |
210 } |
132 QTimer::singleShot(0, this, SLOT(updateRect())); |
211 |
133 } |
212 void ReplayWidget::resizeEvent(QResizeEvent *) |
134 |
|
135 void ReplayWidget::resizeEvent(QResizeEvent *event) |
|
136 { |
213 { |
137 visibleUpdates.clear(); |
214 visibleUpdates.clear(); |
138 |
215 |
139 QRect bounds = rect(); |
216 QRect bounds = rect(); |
140 for (int i = 0; i < updates.size(); ++i) { |
217 |
|
218 int first = qMax(0, from); |
|
219 int last = qMin(unsigned(to), unsigned(updates.size())); |
|
220 for (int i = first; i < last; ++i) { |
141 if (updates.at(i).intersects(bounds)) |
221 if (updates.at(i).intersects(bounds)) |
142 visibleUpdates << i; |
222 visibleUpdates << i; |
143 } |
223 } |
144 |
224 |
145 if (visibleUpdates.size() != updates.size()) |
225 int range = last - first; |
146 printf("Warning: skipped %d frames due to limited resolution\n", updates.size() - visibleUpdates.size()); |
226 |
147 |
227 if (visibleUpdates.size() != range) |
148 } |
228 printf("Warning: skipped %d frames due to limited resolution\n", range - visibleUpdates.size()); |
149 |
229 |
150 ReplayWidget::ReplayWidget(const QString &filename_) |
230 } |
|
231 |
|
232 ReplayWidget::ReplayWidget(const QString &filename_, int from_, int to_, bool single_, int frame_) |
151 : currentFrame(0) |
233 : currentFrame(0) |
152 , currentIteration(0) |
234 , currentIteration(0) |
153 , filename(filename_) |
235 , filename(filename_) |
|
236 , from(from_) |
|
237 , to(to_) |
|
238 , single(single_) |
|
239 , frame(frame_) |
|
240 , currentCommand(1) |
154 { |
241 { |
155 setWindowTitle(filename); |
242 setWindowTitle(filename); |
156 QFile file(filename); |
243 QFile file(filename); |
157 |
244 |
158 if (!file.open(QIODevice::ReadOnly)) { |
245 if (!file.open(QIODevice::ReadOnly)) { |
163 QDataStream in(&file); |
250 QDataStream in(&file); |
164 |
251 |
165 char *data; |
252 char *data; |
166 uint size; |
253 uint size; |
167 in.readBytes(data, size); |
254 in.readBytes(data, size); |
168 bool isTraceFile = size == 7 && qstrncmp(data, "qttrace", 7) == 0; |
255 bool isTraceFile = size >= 7 && qstrncmp(data, "qttrace", 7) == 0; |
169 delete [] data; |
256 |
|
257 uint version = 0; |
|
258 if (size == 9 && qstrncmp(data, "qttraceV2", 9) == 0) { |
|
259 in.setFloatingPointPrecision(QDataStream::SinglePrecision); |
|
260 in >> version; |
|
261 } |
|
262 |
170 if (!isTraceFile) { |
263 if (!isTraceFile) { |
171 printf("File '%s' is not a trace file\n", qPrintable(filename_)); |
264 printf("File '%s' is not a trace file\n", qPrintable(filename_)); |
172 return; |
265 return; |
173 } |
266 } |
174 |
267 |
175 in >> buffer >> updates; |
268 in >> buffer >> updates; |
176 printf("Read paint buffer with %d frames\n", buffer.numFrames()); |
269 printf("Read paint buffer version %d with %d frames\n", version, buffer.numFrames()); |
177 |
270 |
178 resize(buffer.boundingRect().size().toSize()); |
271 resize(buffer.boundingRect().size().toSize()); |
179 |
272 |
180 setAutoFillBackground(false); |
273 setAutoFillBackground(false); |
181 setAttribute(Qt::WA_NoSystemBackground); |
274 setAttribute(Qt::WA_NoSystemBackground); |
187 { |
280 { |
188 QApplication app(argc, argv); |
281 QApplication app(argc, argv); |
189 |
282 |
190 if (argc <= 1 || qstrcmp(argv[1], "-h") == 0 || qstrcmp(argv[1], "--help") == 0) { |
283 if (argc <= 1 || qstrcmp(argv[1], "-h") == 0 || qstrcmp(argv[1], "--help") == 0) { |
191 printf("Replays a tracefile generated with '-graphicssystem trace'\n"); |
284 printf("Replays a tracefile generated with '-graphicssystem trace'\n"); |
192 printf("Usage:\n > %s [traceFile]\n", argv[0]); |
285 printf("Usage:\n > %s [OPTIONS] [traceFile]\n", argv[0]); |
|
286 printf("OPTIONS\n" |
|
287 " --range=from-to to specify a frame range.\n" |
|
288 " --singlerun to do only one run (without statistics)\n" |
|
289 " --instrumentframe=frame to instrument a single frame\n"); |
193 return 1; |
290 return 1; |
194 } |
291 } |
195 |
292 |
196 QFile file(argv[1]); |
293 QFile file(app.arguments().last()); |
197 if (!file.exists()) { |
294 if (!file.exists()) { |
198 printf("%s does not exist\n", argv[1]); |
295 printf("%s does not exist\n", qPrintable(app.arguments().last())); |
199 return 1; |
296 return 1; |
200 } |
297 } |
201 |
298 |
202 ReplayWidget *widget = new ReplayWidget(argv[1]); |
299 bool single = false; |
|
300 |
|
301 int frame = -1; |
|
302 |
|
303 int from = 0; |
|
304 int to = -1; |
|
305 for (int i = 1; i < app.arguments().size() - 1; ++i) { |
|
306 QString arg = app.arguments().at(i); |
|
307 if (arg.startsWith(QLatin1String("--range="))) { |
|
308 QString rest = arg.mid(8); |
|
309 QStringList components = rest.split(QLatin1Char('-')); |
|
310 |
|
311 bool ok1 = false; |
|
312 bool ok2 = false; |
|
313 int fromCandidate = 0; |
|
314 int toCandidate = 0; |
|
315 if (components.size() == 2) { |
|
316 fromCandidate = components.first().toInt(&ok1); |
|
317 toCandidate = components.last().toInt(&ok2); |
|
318 } |
|
319 |
|
320 if (ok1 && ok2) { |
|
321 from = fromCandidate; |
|
322 to = toCandidate; |
|
323 } else { |
|
324 printf("ERROR: malformed syntax in argument %s\n", qPrintable(arg)); |
|
325 } |
|
326 } else if (arg == QLatin1String("--singlerun")) { |
|
327 single = true; |
|
328 } else if (arg.startsWith(QLatin1String("--instrumentframe="))) { |
|
329 QString rest = arg.mid(18); |
|
330 bool ok = false; |
|
331 int frameCandidate = rest.toInt(&ok); |
|
332 if (ok) { |
|
333 frame = frameCandidate; |
|
334 } else { |
|
335 printf("ERROR: malformed syntax in argument %s\n", qPrintable(arg)); |
|
336 } |
|
337 } else { |
|
338 printf("Unrecognized argument: %s\n", qPrintable(arg)); |
|
339 return 1; |
|
340 } |
|
341 } |
|
342 |
|
343 ReplayWidget *widget = new ReplayWidget(app.arguments().last(), from, to, single, frame); |
203 |
344 |
204 if (!widget->updates.isEmpty()) { |
345 if (!widget->updates.isEmpty()) { |
205 widget->show(); |
346 widget->show(); |
206 return app.exec(); |
347 return app.exec(); |
207 } |
348 } |