Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / web / ImageDecodeBench.cpp
blob98f7fa12be195937213699d4b61f55a1bbf9ff9e
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #ifdef WTF
7 Provides a minimal wrapping of the Blink image decoders. Used to perform
8 a non-threaded, memory-to-memory image decode using micro second accuracy
9 clocks to measure image decode time. Optionally applies color correction
10 during image decoding on supported platforms (default off). Usage:
12 % ninja -C /out/Release image_decode_bench &&
13 ./out/Release/image_decode_bench file [iterations]
15 FIXME: Consider adding md5 checksum support to WTF. Use it to compute the
16 decoded image frame md5 and output that value.
18 FIXME: Consider integrating this tool in Chrome telemetry for realz, using
19 the image corpii used to assess Blink image decode performance. Refer to
20 http://crbug.com/398235#c103 and http://crbug.com/258324#c5
22 #endif
24 #include "config.h"
26 #include "platform/SharedBuffer.h"
27 #include "platform/image-decoders/ImageDecoder.h"
28 #include "public/platform/Platform.h"
29 #include "public/web/WebKit.h"
30 #include "wtf/OwnPtr.h"
31 #include "wtf/PassRefPtr.h"
33 #if defined(_WIN32)
34 #if defined(WIN32_LEAN_AND_MEAN)
35 #error Fix: WIN32_LEAN_AND_MEAN disables timeBeginPeriod/TimeEndPeriod.
36 #endif
37 #include <mmsystem.h>
38 #include <sys/stat.h>
39 #include <time.h>
40 #define stat(x,y) _stat(x,y)
41 typedef struct _stat sttype;
42 #else
43 #include <sys/stat.h>
44 #include <sys/time.h>
45 typedef struct stat sttype;
46 #endif
48 using namespace blink;
50 #if defined(_WIN32)
52 // There is no real platform support herein, so adopt the WIN32 performance counter from
53 // WTF http://trac.webkit.org/browser/trunk/Source/WTF/wtf/CurrentTime.cpp?rev=152438
55 static double lowResUTCTime()
57 FILETIME fileTime;
58 GetSystemTimeAsFileTime(&fileTime);
60 // As per Windows documentation for FILETIME, copy the resulting FILETIME structure to a
61 // ULARGE_INTEGER structure using memcpy (using memcpy instead of direct assignment can
62 // prevent alignment faults on 64-bit Windows).
63 ULARGE_INTEGER dateTime;
64 memcpy(&dateTime, &fileTime, sizeof(dateTime));
66 // Number of 100 nanosecond between January 1, 1601 and January 1, 1970.
67 static const ULONGLONG epochBias = 116444736000000000ULL;
68 // Windows file times are in 100s of nanoseconds.
69 static const double hundredsOfNanosecondsPerMillisecond = 10000;
70 return (dateTime.QuadPart - epochBias) / hundredsOfNanosecondsPerMillisecond;
73 static LARGE_INTEGER qpcFrequency;
74 static bool syncedTime;
76 static double highResUpTime()
78 // We use QPC, but only after sanity checking its result, due to bugs:
79 // http://support.microsoft.com/kb/274323 http://support.microsoft.com/kb/895980
80 // http://msdn.microsoft.com/en-us/library/ms644904.aspx ("you can get different results
81 // on different processors due to bugs in the basic input/output system (BIOS) or the
82 // hardware abstraction layer (HAL).").
84 static LARGE_INTEGER qpcLast;
85 static DWORD tickCountLast;
86 static bool inited;
88 LARGE_INTEGER qpc;
89 QueryPerformanceCounter(&qpc);
90 DWORD tickCount = GetTickCount();
92 if (inited) {
93 __int64 qpcElapsed = ((qpc.QuadPart - qpcLast.QuadPart) * 1000) / qpcFrequency.QuadPart;
94 __int64 tickCountElapsed;
95 if (tickCount >= tickCountLast) {
96 tickCountElapsed = (tickCount - tickCountLast);
97 } else {
98 __int64 tickCountLarge = tickCount + 0x100000000I64;
99 tickCountElapsed = tickCountLarge - tickCountLast;
102 // Force a re-sync if QueryPerformanceCounter differs from GetTickCount() by more than
103 // 500ms. (The 500ms value is from http://support.microsoft.com/kb/274323).
104 __int64 diff = tickCountElapsed - qpcElapsed;
105 if (diff > 500 || diff < -500)
106 syncedTime = false;
107 } else {
108 inited = true;
111 qpcLast = qpc;
112 tickCountLast = tickCount;
114 return (1000.0 * qpc.QuadPart) / static_cast<double>(qpcFrequency.QuadPart);
117 static bool qpcAvailable()
119 static bool available;
120 static bool checked;
122 if (checked)
123 return available;
125 available = QueryPerformanceFrequency(&qpcFrequency);
126 checked = true;
127 return available;
130 static double getCurrentTime()
132 // Use a combination of ftime and QueryPerformanceCounter.
133 // ftime returns the information we want, but doesn't have sufficient resolution.
134 // QueryPerformanceCounter has high resolution, but is only usable to measure time intervals.
135 // To combine them, we call ftime and QueryPerformanceCounter initially. Later calls will
136 // use QueryPerformanceCounter by itself, adding the delta to the saved ftime.
137 // We periodically re-sync to correct for drift.
138 static double syncLowResUTCTime;
139 static double syncHighResUpTime;
140 static double lastUTCTime;
142 double lowResTime = lowResUTCTime();
143 if (!qpcAvailable())
144 return lowResTime * (1.0 / 1000.0);
146 double highResTime = highResUpTime();
147 if (!syncedTime) {
148 timeBeginPeriod(1); // increase time resolution around low-res time getter
149 syncLowResUTCTime = lowResTime = lowResUTCTime();
150 timeEndPeriod(1); // restore time resolution
151 syncHighResUpTime = highResTime;
152 syncedTime = true;
155 double highResElapsed = highResTime - syncHighResUpTime;
156 double utc = syncLowResUTCTime + highResElapsed;
158 // Force a clock re-sync if we've drifted.
159 double lowResElapsed = lowResTime - syncLowResUTCTime;
160 const double maximumAllowedDriftMsec = 15.625 * 2.0; // 2x the typical low-res accuracy
161 if (fabs(highResElapsed - lowResElapsed) > maximumAllowedDriftMsec)
162 syncedTime = false;
164 // Make sure time doesn't run backwards (only correct if the difference is < 2 seconds,
165 // since DST or clock changes could occur).
166 const double backwardTimeLimit = 2000.0;
167 if (utc < lastUTCTime && (lastUTCTime - utc) < backwardTimeLimit)
168 return lastUTCTime * (1.0 / 1000.0);
170 lastUTCTime = utc;
171 return utc * (1.0 / 1000.0);
174 #else
176 static double getCurrentTime()
178 struct timeval now;
179 gettimeofday(&now, 0);
180 return now.tv_sec + now.tv_usec * (1.0 / 1000000.0);
183 #endif
185 void getScreenColorProfile(WebVector<char>* profile)
187 static unsigned char profileData[] = {
188 0x00,0x00,0x01,0xea,0x54,0x45,0x53,0x54,0x00,0x00,0x00,0x00,
189 0x6d,0x6e,0x74,0x72,0x52,0x47,0x42,0x20,0x58,0x59,0x5a,0x20,
190 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
191 0x61,0x63,0x73,0x70,0x74,0x65,0x73,0x74,0x00,0x00,0x00,0x00,
192 0x74,0x65,0x73,0x74,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
193 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf6,0xd6,
194 0x00,0x01,0x00,0x00,0x00,0x00,0xd3,0x2d,0x74,0x65,0x73,0x74,
195 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
196 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
197 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
198 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x09,
199 0x63,0x70,0x72,0x74,0x00,0x00,0x00,0xf0,0x00,0x00,0x00,0x0d,
200 0x64,0x65,0x73,0x63,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x8c,
201 0x77,0x74,0x70,0x74,0x00,0x00,0x01,0x8c,0x00,0x00,0x00,0x14,
202 0x72,0x58,0x59,0x5a,0x00,0x00,0x01,0xa0,0x00,0x00,0x00,0x14,
203 0x67,0x58,0x59,0x5a,0x00,0x00,0x01,0xb4,0x00,0x00,0x00,0x14,
204 0x62,0x58,0x59,0x5a,0x00,0x00,0x01,0xc8,0x00,0x00,0x00,0x14,
205 0x72,0x54,0x52,0x43,0x00,0x00,0x01,0xdc,0x00,0x00,0x00,0x0e,
206 0x67,0x54,0x52,0x43,0x00,0x00,0x01,0xdc,0x00,0x00,0x00,0x0e,
207 0x62,0x54,0x52,0x43,0x00,0x00,0x01,0xdc,0x00,0x00,0x00,0x0e,
208 0x74,0x65,0x78,0x74,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
209 0x00,0x00,0x00,0x00,0x64,0x65,0x73,0x63,0x00,0x00,0x00,0x00,
210 0x00,0x00,0x00,0x10,0x77,0x68,0x61,0x63,0x6b,0x65,0x64,0x2e,
211 0x69,0x63,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
212 0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
213 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
214 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
215 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
216 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
217 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
218 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
219 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
220 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
221 0x58,0x59,0x5a,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0xf3,0x52,
222 0x00,0x01,0x00,0x00,0x00,0x01,0x16,0xcc,0x58,0x59,0x5a,0x20,
223 0x00,0x00,0x00,0x00,0x00,0x00,0x34,0x8d,0x00,0x00,0xa0,0x2c,
224 0x00,0x00,0x0f,0x95,0x58,0x59,0x5a,0x20,0x00,0x00,0x00,0x00,
225 0x00,0x00,0x26,0x31,0x00,0x00,0x10,0x2f,0x00,0x00,0xbe,0x9b,
226 0x58,0x59,0x5a,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x9c,0x18,
227 0x00,0x00,0x4f,0xa5,0x00,0x00,0x04,0xfc,0x63,0x75,0x72,0x76,
228 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x33
231 static struct WhackedColorProfile {
233 char* data() { return reinterpret_cast<char*>(profileData); }
235 const size_t profileSize = 490u;
237 size_t size() { return profileSize; }
239 } screenProfile;
241 profile->assign(screenProfile.data(), screenProfile.size());
244 PassRefPtr<SharedBuffer> readFile(const char* fileName)
246 FILE* fp = fopen(fileName, "rb");
247 if (!fp) {
248 fprintf(stderr, "Can't open file %s\n", fileName);
249 exit(2);
252 sttype s;
253 stat(fileName, &s);
254 size_t fileSize = s.st_size;
255 if (s.st_size <= 0)
256 return SharedBuffer::create();
258 OwnPtr<unsigned char[]> buffer = adoptArrayPtr(new unsigned char[fileSize]);
259 if (fileSize != fread(buffer.get(), 1, fileSize, fp)) {
260 fprintf(stderr, "Error reading file %s\n", fileName);
261 exit(2);
264 fclose(fp);
265 return SharedBuffer::create(buffer.get(), fileSize);
268 bool decodeImageData(SharedBuffer* data, bool colorCorrection, size_t packetSize)
270 OwnPtr<ImageDecoder> decoder = ImageDecoder::create(*data,
271 ImageDecoder::AlphaPremultiplied, colorCorrection ?
272 ImageDecoder::GammaAndColorProfileApplied : ImageDecoder::GammaAndColorProfileIgnored);
274 if (!packetSize) {
275 bool allDataReceived = true;
276 decoder->setData(data, allDataReceived);
278 int frameCount = decoder->frameCount();
279 for (int i = 0; i < frameCount; ++i) {
280 if (!decoder->frameBufferAtIndex(i))
281 return false;
284 return !decoder->failed();
287 RefPtr<SharedBuffer> packetData = SharedBuffer::create();
288 unsigned position = 0;
289 while (true) {
290 const char* packet;
291 unsigned length = data->getSomeData(packet, position);
293 length = std::min(static_cast<size_t>(length), packetSize);
294 packetData->append(packet, length);
295 position += length;
297 bool allDataReceived = position == data->size();
298 decoder->setData(packetData.get(), allDataReceived);
300 int frameCount = decoder->frameCount();
301 for (int i = 0; i < frameCount; ++i) {
302 if (!decoder->frameBufferAtIndex(i))
303 break;
306 if (allDataReceived || decoder->failed())
307 break;
310 return !decoder->failed();
313 int main(int argc, char* argv[])
315 char* name = argv[0];
317 // If the platform supports color correction, allow it to be controlled.
319 bool applyColorCorrection = false;
321 #if USE(QCMSLIB)
322 if (argc >= 2 && strcmp(argv[1], "--color-correct") == 0)
323 applyColorCorrection = (--argc, ++argv, true);
325 if (argc < 2) {
326 fprintf(stderr, "Usage: %s [--color-correct] file [iterations] [packetSize]\n", name);
327 exit(1);
329 #else
330 if (argc < 2) {
331 fprintf(stderr, "Usage: %s file [iterations] [packetSize]\n", name);
332 exit(1);
334 #endif
336 // Control decode bench iterations and packet size.
338 size_t iterations = 1;
339 if (argc >= 3) {
340 char* end = 0;
341 iterations = strtol(argv[2], &end, 10);
342 if (*end != '\0' || !iterations) {
343 fprintf(stderr, "Second argument should be number of iterations. "
344 "The default is 1. You supplied %s\n", argv[2]);
345 exit(1);
349 size_t packetSize = 0;
350 if (argc >= 4) {
351 char* end = 0;
352 packetSize = strtol(argv[3], &end, 10);
353 if (*end != '\0') {
354 fprintf(stderr, "Third argument should be packet size. Default is "
355 "0, meaning to decode the entire image in one packet. You "
356 "supplied %s\n", argv[3]);
357 exit(1);
361 // Create a web platform without V8.
363 class WebPlatform : public blink::Platform {
364 public:
365 const unsigned char* getTraceCategoryEnabledFlag(const char*) override
367 return reinterpret_cast<const unsigned char *>("nope-none-nada");
370 void cryptographicallyRandomValues(unsigned char*, size_t) override
372 // Do nothing: make blink::Platform use the default crypto-randoms.
375 void screenColorProfile(WebVector<char>* profile) override
377 getScreenColorProfile(profile); // Returns a whacked color profile.
381 blink::initializeWithoutV8(new WebPlatform());
383 // Set image decoding Platform options.
385 #if USE(QCMSLIB)
386 ImageDecoder::qcmsOutputDeviceProfile(); // Initialize screen colorProfile.
387 #endif
389 // Read entire file content to data.
391 RefPtr<SharedBuffer> data = readFile(argv[1]);
392 if (!data.get() || !data->size()) {
393 fprintf(stderr, "Error reading image data from [%s]\n", argv[1]);
394 exit(2);
397 // Consolidate the SharedBuffer data segments into one, contiguous block of memory.
398 data->data();
400 // Image decode bench for iterations.
402 double totalTime = 0.0;
404 for (size_t i = 0; i < iterations; ++i) {
405 double startTime = getCurrentTime();
406 bool decoded = decodeImageData(data.get(), applyColorCorrection, packetSize);
407 double elapsedTime = getCurrentTime() - startTime;
408 totalTime += elapsedTime;
409 if (!decoded) {
410 fprintf(stderr, "Image decode failed [%s]\n", argv[1]);
411 exit(3);
415 // Results to stdout.
417 double averageTime = totalTime / static_cast<double>(iterations);
418 printf("%f %f\n", totalTime, averageTime);
419 return 0;