Warning: That file was not part of the compilation database. It may have many parsing errors.
1 | /**************************************************************************** |
---|---|
2 | ** |
3 | ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). |
4 | ** Contact: http://www.qt-project.org/legal |
5 | ** |
6 | ** This file is part of the tools applications of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and Digia. For licensing terms and |
14 | ** conditions see http://qt.digia.com/licensing. For further information |
15 | ** use the contact form at http://qt.digia.com/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 2.1 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 2.1 requirements |
23 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
24 | ** |
25 | ** In addition, as a special exception, Digia gives you certain additional |
26 | ** rights. These rights are described in the Digia Qt LGPL Exception |
27 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
28 | ** |
29 | ** GNU General Public License Usage |
30 | ** Alternatively, this file may be used under the terms of the GNU |
31 | ** General Public License version 3.0 as published by the Free Software |
32 | ** Foundation and appearing in the file LICENSE.GPL included in the |
33 | ** packaging of this file. Please review the following information to |
34 | ** ensure the GNU General Public License version 3.0 requirements will be |
35 | ** met: http://www.gnu.org/copyleft/gpl.html. |
36 | ** |
37 | ** |
38 | ** $QT_END_LICENSE$ |
39 | ** |
40 | ****************************************************************************/ |
41 | |
42 | #include <QtGui> |
43 | #include <QtDebug> |
44 | |
45 | #include <private/qpaintengineex_p.h> |
46 | #include <private/qpaintbuffer_p.h> |
47 | |
48 | class ReplayWidget : public QWidget |
49 | { |
50 | Q_OBJECT |
51 | public: |
52 | ReplayWidget(const QString &filename, int from, int to, bool single, int frame); |
53 | |
54 | void paintEvent(QPaintEvent *event); |
55 | void resizeEvent(QResizeEvent *event); |
56 | |
57 | public slots: |
58 | void updateRect(); |
59 | |
60 | public: |
61 | QList<QRegion> updates; |
62 | QPaintBuffer buffer; |
63 | |
64 | int currentFrame; |
65 | int currentIteration; |
66 | QTime timer; |
67 | |
68 | QList<uint> visibleUpdates; |
69 | |
70 | QVector<uint> iterationTimes; |
71 | QString filename; |
72 | |
73 | int from; |
74 | int to; |
75 | |
76 | bool single; |
77 | |
78 | int frame; |
79 | int currentCommand; |
80 | }; |
81 | |
82 | void ReplayWidget::updateRect() |
83 | { |
84 | if (frame >= 0 && !updates.isEmpty()) |
85 | update(updates.at(frame)); |
86 | else if (!visibleUpdates.isEmpty()) |
87 | update(updates.at(visibleUpdates.at(currentFrame))); |
88 | } |
89 | |
90 | const int singleFrameRepeatsPerCommand = 100; |
91 | const int singleFrameIterations = 4; |
92 | |
93 | void ReplayWidget::paintEvent(QPaintEvent *) |
94 | { |
95 | QPainter p(this); |
96 | |
97 | QTimer::singleShot(0, this, SLOT(updateRect())); |
98 | |
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 | } |
158 | |
159 | buffer.draw(&p, visibleUpdates.at(currentFrame)); |
160 | |
161 | ++currentFrame; |
162 | if (currentFrame >= visibleUpdates.size()) { |
163 | currentFrame = 0; |
164 | ++currentIteration; |
165 | |
166 | if (single) { |
167 | deleteLater(); |
168 | return; |
169 | } |
170 | |
171 | if (currentIteration == 3) |
172 | timer.start(); |
173 | else if (currentIteration > 3) { |
174 | iterationTimes << timer.elapsed(); |
175 | timer.restart(); |
176 | |
177 | if (iterationTimes.size() >= 3) { |
178 | qreal mean = 0; |
179 | qreal stddev = 0; |
180 | uint min = INT_MAX; |
181 | |
182 | for (int i = 0; i < iterationTimes.size(); ++i) { |
183 | mean += iterationTimes.at(i); |
184 | min = qMin(min, iterationTimes.at(i)); |
185 | } |
186 | |
187 | mean /= qreal(iterationTimes.size()); |
188 | |
189 | for (int i = 0; i < iterationTimes.size(); ++i) { |
190 | qreal delta = iterationTimes.at(i) - mean; |
191 | stddev += delta * delta; |
192 | } |
193 | |
194 | stddev = qSqrt(stddev / iterationTimes.size()); |
195 | |
196 | qSort(iterationTimes.begin(), iterationTimes.end()); |
197 | uint median = iterationTimes.at(iterationTimes.size() / 2); |
198 | |
199 | stddev = 100 * stddev / mean; |
200 | |
201 | if (iterationTimes.size() >= 10 || stddev < 4) { |
202 | printf("%s, iterations: %d, frames: %d, min(ms): %d, median(ms): %d, stddev: %f %%, max(fps): %f\n", qPrintable(filename), |
203 | iterationTimes.size(), visibleUpdates.size(), min, median, stddev, 1000. * visibleUpdates.size() / min); |
204 | deleteLater(); |
205 | return; |
206 | } |
207 | } |
208 | } |
209 | } |
210 | } |
211 | |
212 | void ReplayWidget::resizeEvent(QResizeEvent *) |
213 | { |
214 | visibleUpdates.clear(); |
215 | |
216 | QRect bounds = rect(); |
217 | |
218 | int first = qMax(0, from); |
219 | int last = qMin(unsigned(to), unsigned(updates.size())); |
220 | for (int i = first; i < last; ++i) { |
221 | if (updates.at(i).intersects(bounds)) |
222 | visibleUpdates << i; |
223 | } |
224 | |
225 | int range = last - first; |
226 | |
227 | if (visibleUpdates.size() != range) |
228 | printf("Warning: skipped %d frames due to limited resolution\n", range - visibleUpdates.size()); |
229 | |
230 | } |
231 | |
232 | ReplayWidget::ReplayWidget(const QString &filename_, int from_, int to_, bool single_, int frame_) |
233 | : currentFrame(0) |
234 | , currentIteration(0) |
235 | , filename(filename_) |
236 | , from(from_) |
237 | , to(to_) |
238 | , single(single_) |
239 | , frame(frame_) |
240 | , currentCommand(1) |
241 | { |
242 | setWindowTitle(filename); |
243 | QFile file(filename); |
244 | |
245 | if (!file.open(QIODevice::ReadOnly)) { |
246 | printf("Failed to load input file '%s'\n", qPrintable(filename_)); |
247 | return; |
248 | } |
249 | |
250 | QDataStream in(&file); |
251 | |
252 | char *data; |
253 | uint size; |
254 | in.readBytes(data, size); |
255 | bool isTraceFile = size >= 7 && qstrncmp(data, "qttrace", 7) == 0; |
256 | |
257 | uint version = 0; |
258 | if (size == 9 && qstrncmp(data, "qttraceV2", 9) == 0) { |
259 | in.setFloatingPointPrecision(QDataStream::SinglePrecision); |
260 | in >> version; |
261 | } |
262 | |
263 | if (!isTraceFile) { |
264 | printf("File '%s' is not a trace file\n", qPrintable(filename_)); |
265 | return; |
266 | } |
267 | |
268 | in >> buffer >> updates; |
269 | printf("Read paint buffer version %d with %d frames\n", version, buffer.numFrames()); |
270 | |
271 | resize(buffer.boundingRect().size().toSize()); |
272 | |
273 | setAutoFillBackground(false); |
274 | setAttribute(Qt::WA_NoSystemBackground); |
275 | |
276 | QTimer::singleShot(10, this, SLOT(updateRect())); |
277 | } |
278 | |
279 | int main(int argc, char **argv) |
280 | { |
281 | QApplication app(argc, argv); |
282 | |
283 | if (argc <= 1 || qstrcmp(argv[1], "-h") == 0 || qstrcmp(argv[1], "--help") == 0) { |
284 | printf("Replays a tracefile generated with '-graphicssystem trace'\n"); |
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"); |
290 | return 1; |
291 | } |
292 | |
293 | QFile file(app.arguments().last()); |
294 | if (!file.exists()) { |
295 | printf("%s does not exist\n", qPrintable(app.arguments().last())); |
296 | return 1; |
297 | } |
298 | |
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); |
344 | |
345 | if (!widget->updates.isEmpty()) { |
346 | widget->show(); |
347 | return app.exec(); |
348 | } |
349 | |
350 | } |
351 | #include "main.moc" |
352 |
Warning: That file was not part of the compilation database. It may have many parsing errors.